00db10
From 835c3bf23a119a7fcb8c70734d4fdf49461d8195 Mon Sep 17 00:00:00 2001
00db10
From: Stefan Liebler <stli@linux.vnet.ibm.com>
00db10
Date: Mon, 7 Nov 2016 16:14:07 +0100
00db10
Subject: [PATCH 09/17] S390: Optimize utf8-utf32 module.
00db10
00db10
Upstream commit 421c5278d83e72740150259960a431706ac343f9
00db10
00db10
This patch reworks the s390 specific module to convert between utf8 and utf32.
00db10
Now ifunc is used to choose either the c or etf3eh (with convert utf
00db10
instruction) variants at runtime.
00db10
Furthermore a new vector variant for z13 is introduced which will be build
00db10
and chosen if vector support is available at build / runtime.
00db10
The vector variants optimize input of 1byte utf8 characters. The convert utf
00db10
instruction is used if a multibyte utf8 character is found.
00db10
00db10
This patch also fixes some whitespace errors. The c variants are rejecting
00db10
UTF-16 surrogates and values above 0x10ffff now.
00db10
Furthermore, the etf3eh variants are handling the "UTF-xx//IGNORE" case now.
00db10
Before they ignored the ignore-case and always stopped at an error.
00db10
00db10
ChangeLog:
00db10
00db10
	* sysdeps/s390/s390-64/utf8-utf32-z9.c: Use ifunc to select c, etf3eh
00db10
	or new vector loop-variant.
00db10
---
00db10
 sysdeps/s390/s390-64/utf8-utf32-z9.c | 664 +++++++++++++++++++++++++----------
00db10
 1 file changed, 480 insertions(+), 184 deletions(-)
00db10
00db10
diff --git a/sysdeps/s390/s390-64/utf8-utf32-z9.c b/sysdeps/s390/s390-64/utf8-utf32-z9.c
00db10
index 721279e..1ce5ac5 100644
00db10
--- a/sysdeps/s390/s390-64/utf8-utf32-z9.c
00db10
+++ b/sysdeps/s390/s390-64/utf8-utf32-z9.c
00db10
@@ -30,35 +30,25 @@
00db10
 #include <dl-procinfo.h>
00db10
 #include <gconv.h>
00db10
 
00db10
-/* UTF-32 big endian byte order mark.  */
00db10
-#define BOM	                0x0000feffu
00db10
+#if defined HAVE_S390_VX_GCC_SUPPORT
00db10
+# define ASM_CLOBBER_VR(NR) , NR
00db10
+#else
00db10
+# define ASM_CLOBBER_VR(NR)
00db10
+#endif
00db10
 
00db10
+/* Defines for skeleton.c.  */
00db10
 #define DEFINE_INIT		0
00db10
 #define DEFINE_FINI		0
00db10
-/* These definitions apply to the UTF-8 to UTF-32 direction.  The
00db10
-   software implementation for UTF-8 still supports multibyte
00db10
-   characters up to 6 bytes whereas the hardware variant does not.  */
00db10
 #define MIN_NEEDED_FROM		1
00db10
 #define MAX_NEEDED_FROM		6
00db10
 #define MIN_NEEDED_TO		4
00db10
-#define FROM_LOOP		from_utf8_loop
00db10
-#define TO_LOOP			to_utf8_loop
00db10
+#define FROM_LOOP		__from_utf8_loop
00db10
+#define TO_LOOP			__to_utf8_loop
00db10
 #define FROM_DIRECTION		(dir == from_utf8)
00db10
 #define ONE_DIRECTION           0
00db10
-#define PREPARE_LOOP							\
00db10
-  enum direction dir = ((struct utf8_data *) step->__data)->dir;	\
00db10
-  int emit_bom = ((struct utf8_data *) step->__data)->emit_bom;		\
00db10
-									\
00db10
-  if (emit_bom && !data->__internal_use					\
00db10
-      && data->__invocation_counter == 0)				\
00db10
-    {									\
00db10
-      /* Emit the Byte Order Mark.  */					\
00db10
-      if (__glibc_unlikely (outbuf + 4 > outend))			      \
00db10
-	return __GCONV_FULL_OUTPUT;					\
00db10
-									\
00db10
-      put32u (outbuf, BOM);						\
00db10
-      outbuf += 4;							\
00db10
-    }
00db10
+
00db10
+/* UTF-32 big endian byte order mark.  */
00db10
+#define BOM			0x0000feffu
00db10
 
00db10
 /* Direction of the transformation.  */
00db10
 enum direction
00db10
@@ -155,16 +145,16 @@ gconv_end (struct __gconv_step *data)
00db10
     register unsigned long long outlen __asm__("11") = outend - outptr;	\
00db10
     uint64_t cc = 0;							\
00db10
 									\
00db10
-    __asm__ volatile (".machine push       \n\t"			\
00db10
-		      ".machine \"z9-109\" \n\t"			\
00db10
-		      "0: " INSTRUCTION "  \n\t"			\
00db10
-		      ".machine pop        \n\t"			\
00db10
-		      "   jo     0b        \n\t"			\
00db10
-		      "   ipm    %2        \n"				\
00db10
-		      : "+a" (pOutput), "+a" (pInput), "+d" (cc),	\
00db10
-		      "+d" (outlen), "+d" (inlen)			\
00db10
-		      :							\
00db10
-		      : "cc", "memory");				\
00db10
+    __asm__ __volatile__ (".machine push       \n\t"			\
00db10
+			  ".machine \"z9-109\" \n\t"			\
00db10
+			  "0: " INSTRUCTION "  \n\t"			\
00db10
+			  ".machine pop        \n\t"			\
00db10
+			  "   jo     0b        \n\t"			\
00db10
+			  "   ipm    %2        \n"			\
00db10
+			  : "+a" (pOutput), "+a" (pInput), "+d" (cc),	\
00db10
+			    "+d" (outlen), "+d" (inlen)			\
00db10
+			  :						\
00db10
+			  : "cc", "memory");				\
00db10
 									\
00db10
     inptr = pInput;							\
00db10
     outptr = pOutput;							\
00db10
@@ -173,49 +163,150 @@ gconv_end (struct __gconv_step *data)
00db10
     if (cc == 1)							\
00db10
       {									\
00db10
 	result = __GCONV_FULL_OUTPUT;					\
00db10
-	break;								\
00db10
       }									\
00db10
     else if (cc == 2)							\
00db10
       {									\
00db10
 	result = __GCONV_ILLEGAL_INPUT;					\
00db10
-	break;								\
00db10
       }									\
00db10
   }
00db10
 
00db10
+#define PREPARE_LOOP							\
00db10
+  enum direction dir = ((struct utf8_data *) step->__data)->dir;	\
00db10
+  int emit_bom = ((struct utf8_data *) step->__data)->emit_bom;		\
00db10
+									\
00db10
+  if (emit_bom && !data->__internal_use					\
00db10
+      && data->__invocation_counter == 0)				\
00db10
+    {									\
00db10
+      /* Emit the Byte Order Mark.  */					\
00db10
+      if (__glibc_unlikely (outbuf + 4 > outend))			\
00db10
+	return __GCONV_FULL_OUTPUT;					\
00db10
+									\
00db10
+      put32u (outbuf, BOM);						\
00db10
+      outbuf += 4;							\
00db10
+    }
00db10
+
00db10
 /* Conversion function from UTF-8 to UTF-32 internal/BE.  */
00db10
 
00db10
-#define MIN_NEEDED_INPUT	MIN_NEEDED_FROM
00db10
-#define MAX_NEEDED_INPUT	MAX_NEEDED_FROM
00db10
-#define MIN_NEEDED_OUTPUT	MIN_NEEDED_TO
00db10
-#define LOOPFCT			FROM_LOOP
00db10
-/* The software routine is copied from gconv_simple.c.  */
00db10
-#define BODY								\
00db10
+#define STORE_REST_COMMON						      \
00db10
+  {									      \
00db10
+    /* We store the remaining bytes while converting them into the UCS4	      \
00db10
+       format.  We can assume that the first byte in the buffer is	      \
00db10
+       correct and that it requires a larger number of bytes than there	      \
00db10
+       are in the input buffer.  */					      \
00db10
+    wint_t ch = **inptrp;						      \
00db10
+    size_t cnt, r;							      \
00db10
+									      \
00db10
+    state->__count = inend - *inptrp;					      \
00db10
+									      \
00db10
+    assert (ch != 0xc0 && ch != 0xc1);					      \
00db10
+    if (ch >= 0xc2 && ch < 0xe0)					      \
00db10
+      {									      \
00db10
+	/* We expect two bytes.  The first byte cannot be 0xc0 or	      \
00db10
+	   0xc1, otherwise the wide character could have been		      \
00db10
+	   represented using a single byte.  */				      \
00db10
+	cnt = 2;							      \
00db10
+	ch &= 0x1f;							      \
00db10
+      }									      \
00db10
+    else if (__glibc_likely ((ch & 0xf0) == 0xe0))			      \
00db10
+      {									      \
00db10
+	/* We expect three bytes.  */					      \
00db10
+	cnt = 3;							      \
00db10
+	ch &= 0x0f;							      \
00db10
+      }									      \
00db10
+    else if (__glibc_likely ((ch & 0xf8) == 0xf0))			      \
00db10
+      {									      \
00db10
+	/* We expect four bytes.  */					      \
00db10
+	cnt = 4;							      \
00db10
+	ch &= 0x07;							      \
00db10
+      }									      \
00db10
+    else if (__glibc_likely ((ch & 0xfc) == 0xf8))			      \
00db10
+      {									      \
00db10
+	/* We expect five bytes.  */					      \
00db10
+	cnt = 5;							      \
00db10
+	ch &= 0x03;							      \
00db10
+      }									      \
00db10
+    else								      \
00db10
+      {									      \
00db10
+	/* We expect six bytes.  */					      \
00db10
+	cnt = 6;							      \
00db10
+	ch &= 0x01;							      \
00db10
+      }									      \
00db10
+									      \
00db10
+    /* The first byte is already consumed.  */				      \
00db10
+    r = cnt - 1;							      \
00db10
+    while (++(*inptrp) < inend)						      \
00db10
+      {									      \
00db10
+	ch <<= 6;							      \
00db10
+	ch |= **inptrp & 0x3f;						      \
00db10
+	--r;								      \
00db10
+      }									      \
00db10
+									      \
00db10
+    /* Shift for the so far missing bytes.  */				      \
00db10
+    ch <<= r * 6;							      \
00db10
+									      \
00db10
+    /* Store the number of bytes expected for the entire sequence.  */	      \
00db10
+    state->__count |= cnt << 8;						      \
00db10
+									      \
00db10
+    /* Store the value.  */						      \
00db10
+    state->__value.__wch = ch;						      \
00db10
+  }
00db10
+
00db10
+#define UNPACK_BYTES_COMMON \
00db10
+  {									      \
00db10
+    static const unsigned char inmask[5] = { 0xc0, 0xe0, 0xf0, 0xf8, 0xfc };  \
00db10
+    wint_t wch = state->__value.__wch;					      \
00db10
+    size_t ntotal = state->__count >> 8;				      \
00db10
+									      \
00db10
+    inlen = state->__count & 255;					      \
00db10
+									      \
00db10
+    bytebuf[0] = inmask[ntotal - 2];					      \
00db10
+									      \
00db10
+    do									      \
00db10
+      {									      \
00db10
+	if (--ntotal < inlen)						      \
00db10
+	  bytebuf[ntotal] = 0x80 | (wch & 0x3f);			      \
00db10
+	wch >>= 6;							      \
00db10
+      }									      \
00db10
+    while (ntotal > 1);							      \
00db10
+									      \
00db10
+    bytebuf[0] |= wch;							      \
00db10
+  }
00db10
+
00db10
+#define CLEAR_STATE_COMMON \
00db10
+  state->__count = 0
00db10
+
00db10
+#define BODY_FROM_HW(ASM)						\
00db10
   {									\
00db10
-    if (GLRO (dl_hwcap) & HWCAP_S390_ETF3EH)				\
00db10
-      {									\
00db10
-	HARDWARE_CONVERT ("cu14 %0, %1, 1");				\
00db10
+    ASM;								\
00db10
+    if (__glibc_likely (inptr == inend)					\
00db10
+	|| result == __GCONV_FULL_OUTPUT)				\
00db10
+      break;								\
00db10
 									\
00db10
-	if (inptr != inend)						\
00db10
-	  {								\
00db10
-	    int i;							\
00db10
-	    for (i = 1; inptr + i < inend; ++i)				\
00db10
-	      if ((inptr[i] & 0xc0) != 0x80)				\
00db10
-		break;							\
00db10
+    int i;								\
00db10
+    for (i = 1; inptr + i < inend && i < 5; ++i)			\
00db10
+      if ((inptr[i] & 0xc0) != 0x80)					\
00db10
+	break;								\
00db10
 									\
00db10
-	    if (__glibc_likely (inptr + i == inend))			      \
00db10
-	      {								\
00db10
-		result = __GCONV_INCOMPLETE_INPUT;			\
00db10
-		break;							\
00db10
-	      }								\
00db10
-	    STANDARD_FROM_LOOP_ERR_HANDLER (i);				\
00db10
-	  }								\
00db10
-	continue;							\
00db10
+    if (__glibc_likely (inptr + i == inend				\
00db10
+			&& result == __GCONV_EMPTY_INPUT))		\
00db10
+      {									\
00db10
+	result = __GCONV_INCOMPLETE_INPUT;				\
00db10
+	break;								\
00db10
       }									\
00db10
-									\
00db10
+    STANDARD_FROM_LOOP_ERR_HANDLER (i);					\
00db10
+  }
00db10
+
00db10
+/* This hardware routine uses the Convert UTF8 to UTF32 (cu14) instruction.  */
00db10
+#define BODY_FROM_ETF3EH BODY_FROM_HW (HARDWARE_CONVERT ("cu14 %0, %1, 1"))
00db10
+
00db10
+
00db10
+/* The software routine is copied from gconv_simple.c.  */
00db10
+#define BODY_FROM_C							\
00db10
+  {									\
00db10
     /* Next input byte.  */						\
00db10
     uint32_t ch = *inptr;						\
00db10
 									\
00db10
-    if (__glibc_likely (ch < 0x80))					      \
00db10
+    if (__glibc_likely (ch < 0x80))					\
00db10
       {									\
00db10
 	/* One byte sequence.  */					\
00db10
 	++inptr;							\
00db10
@@ -233,30 +324,18 @@ gconv_end (struct __gconv_step *data)
00db10
 	    cnt = 2;							\
00db10
 	    ch &= 0x1f;							\
00db10
 	  }								\
00db10
-        else if (__glibc_likely ((ch & 0xf0) == 0xe0))			      \
00db10
+	else if (__glibc_likely ((ch & 0xf0) == 0xe0))			\
00db10
 	  {								\
00db10
 	    /* We expect three bytes.  */				\
00db10
 	    cnt = 3;							\
00db10
 	    ch &= 0x0f;							\
00db10
 	  }								\
00db10
-	else if (__glibc_likely ((ch & 0xf8) == 0xf0))			      \
00db10
+	else if (__glibc_likely ((ch & 0xf8) == 0xf0))			\
00db10
 	  {								\
00db10
 	    /* We expect four bytes.  */				\
00db10
 	    cnt = 4;							\
00db10
 	    ch &= 0x07;							\
00db10
 	  }								\
00db10
-	else if (__glibc_likely ((ch & 0xfc) == 0xf8))			      \
00db10
-	  {								\
00db10
-	    /* We expect five bytes.  */				\
00db10
-	    cnt = 5;							\
00db10
-	    ch &= 0x03;							\
00db10
-	  }								\
00db10
-	else if (__glibc_likely ((ch & 0xfe) == 0xfc))			      \
00db10
-	  {								\
00db10
-	    /* We expect six bytes.  */					\
00db10
-	    cnt = 6;							\
00db10
-	    ch &= 0x01;							\
00db10
-	  }								\
00db10
 	else								\
00db10
 	  {								\
00db10
 	    /* Search the end of this ill-formed UTF-8 character.  This	\
00db10
@@ -272,7 +351,7 @@ gconv_end (struct __gconv_step *data)
00db10
 	    STANDARD_FROM_LOOP_ERR_HANDLER (i);				\
00db10
 	  }								\
00db10
 									\
00db10
-	if (__glibc_unlikely (inptr + cnt > inend))			      \
00db10
+	if (__glibc_unlikely (inptr + cnt > inend))			\
00db10
 	  {								\
00db10
 	    /* We don't have enough input.  But before we report	\
00db10
 	       that check that all the bytes are correct.  */		\
00db10
@@ -280,7 +359,7 @@ gconv_end (struct __gconv_step *data)
00db10
 	      if ((inptr[i] & 0xc0) != 0x80)				\
00db10
 		break;							\
00db10
 									\
00db10
-	    if (__glibc_likely (inptr + i == inend))			      \
00db10
+	    if (__glibc_likely (inptr + i == inend))			\
00db10
 	      {								\
00db10
 		result = __GCONV_INCOMPLETE_INPUT;			\
00db10
 		break;							\
00db10
@@ -305,7 +384,10 @@ gconv_end (struct __gconv_step *data)
00db10
 	/* If i < cnt, some trail byte was not >= 0x80, < 0xc0.		\
00db10
 	   If cnt > 2 and ch < 2^(5*cnt-4), the wide character ch could	\
00db10
 	   have been represented with fewer than cnt bytes.  */		\
00db10
-	if (i < cnt || (cnt > 2 && (ch >> (5 * cnt - 4)) == 0))		\
00db10
+	if (i < cnt || (cnt > 2 && (ch >> (5 * cnt - 4)) == 0)		\
00db10
+	    /* Do not accept UTF-16 surrogates.  */			\
00db10
+	    || (ch >= 0xd800 && ch <= 0xdfff)				\
00db10
+	    || (ch > 0x10ffff))						\
00db10
 	  {								\
00db10
 	    /* This is an illegal encoding.  */				\
00db10
 	    goto errout;						\
00db10
@@ -318,137 +400,212 @@ gconv_end (struct __gconv_step *data)
00db10
     *((uint32_t *) outptr) = ch;					\
00db10
     outptr += sizeof (uint32_t);					\
00db10
   }
00db10
-#define LOOP_NEED_FLAGS
00db10
 
00db10
-#define STORE_REST							\
00db10
-  {									      \
00db10
-    /* We store the remaining bytes while converting them into the UCS4	      \
00db10
-       format.  We can assume that the first byte in the buffer is	      \
00db10
-       correct and that it requires a larger number of bytes than there	      \
00db10
-       are in the input buffer.  */					      \
00db10
-    wint_t ch = **inptrp;						      \
00db10
-    size_t cnt, r;							      \
00db10
-									      \
00db10
-    state->__count = inend - *inptrp;					      \
00db10
-									      \
00db10
-    if (ch >= 0xc2 && ch < 0xe0)					      \
00db10
-      {									      \
00db10
-	/* We expect two bytes.  The first byte cannot be 0xc0 or	      \
00db10
-	   0xc1, otherwise the wide character could have been		      \
00db10
-	   represented using a single byte.  */				      \
00db10
-	cnt = 2;							      \
00db10
-	ch &= 0x1f;							      \
00db10
-      }									      \
00db10
-    else if (__glibc_likely ((ch & 0xf0) == 0xe0))			      \
00db10
-      {									      \
00db10
-	/* We expect three bytes.  */					      \
00db10
-	cnt = 3;							      \
00db10
-	ch &= 0x0f;							      \
00db10
-      }									      \
00db10
-    else if (__glibc_likely ((ch & 0xf8) == 0xf0))			      \
00db10
-      {									      \
00db10
-	/* We expect four bytes.  */					      \
00db10
-	cnt = 4;							      \
00db10
-	ch &= 0x07;							      \
00db10
-      }									      \
00db10
-    else if (__glibc_likely ((ch & 0xfc) == 0xf8))			      \
00db10
-      {									      \
00db10
-	/* We expect five bytes.  */					      \
00db10
-	cnt = 5;							      \
00db10
-	ch &= 0x03;							      \
00db10
-      }									      \
00db10
-    else								      \
00db10
-      {									      \
00db10
-	/* We expect six bytes.  */					      \
00db10
-	cnt = 6;							      \
00db10
-	ch &= 0x01;							      \
00db10
-      }									      \
00db10
-									      \
00db10
-    /* The first byte is already consumed.  */				      \
00db10
-    r = cnt - 1;							      \
00db10
-    while (++(*inptrp) < inend)						      \
00db10
-      {									      \
00db10
-	ch <<= 6;							      \
00db10
-	ch |= **inptrp & 0x3f;						      \
00db10
-	--r;								      \
00db10
-      }									      \
00db10
-									      \
00db10
-    /* Shift for the so far missing bytes.  */				      \
00db10
-    ch <<= r * 6;							      \
00db10
-									      \
00db10
-    /* Store the number of bytes expected for the entire sequence.  */	      \
00db10
-    state->__count |= cnt << 8;						      \
00db10
-									      \
00db10
-    /* Store the value.  */						      \
00db10
-    state->__value.__wch = ch;						      \
00db10
+#define HW_FROM_VX							\
00db10
+  {									\
00db10
+    register const unsigned char* pInput asm ("8") = inptr;		\
00db10
+    register size_t inlen asm ("9") = inend - inptr;			\
00db10
+    register unsigned char* pOutput asm ("10") = outptr;		\
00db10
+    register size_t outlen asm("11") = outend - outptr;			\
00db10
+    unsigned long tmp, tmp2, tmp3;					\
00db10
+    asm volatile (".machine push\n\t"					\
00db10
+		  ".machine \"z13\"\n\t"				\
00db10
+		  ".machinemode \"zarch_nohighgprs\"\n\t"		\
00db10
+		  "    vrepib %%v30,0x7f\n\t" /* For compare > 0x7f.  */ \
00db10
+		  "    vrepib %%v31,0x20\n\t"				\
00db10
+		  /* Loop which handles UTF-8 chars <=0x7f.  */		\
00db10
+		  "0:  clgijl %[R_INLEN],16,20f\n\t"			\
00db10
+		  "    clgijl %[R_OUTLEN],64,20f\n\t"			\
00db10
+		  "1: vl %%v16,0(%[R_IN])\n\t"				\
00db10
+		  "    vstrcbs %%v17,%%v16,%%v30,%%v31\n\t"		\
00db10
+		  "    jno 10f\n\t" /* Jump away if not all bytes are 1byte \
00db10
+				   UTF8 chars.  */			\
00db10
+		  /* Enlarge to UCS4.  */				\
00db10
+		  "    vuplhb %%v18,%%v16\n\t"				\
00db10
+		  "    vupllb %%v19,%%v16\n\t"				\
00db10
+		  "    la %[R_IN],16(%[R_IN])\n\t"			\
00db10
+		  "    vuplhh %%v20,%%v18\n\t"				\
00db10
+		  "    aghi %[R_INLEN],-16\n\t"				\
00db10
+		  "    vupllh %%v21,%%v18\n\t"				\
00db10
+		  "    aghi %[R_OUTLEN],-64\n\t"			\
00db10
+		  "    vuplhh %%v22,%%v19\n\t"				\
00db10
+		  "    vupllh %%v23,%%v19\n\t"				\
00db10
+		  /* Store 64 bytes to buf_out.  */			\
00db10
+		  "    vstm %%v20,%%v23,0(%[R_OUT])\n\t"		\
00db10
+		  "    la %[R_OUT],64(%[R_OUT])\n\t"			\
00db10
+		  "    clgijl %[R_INLEN],16,20f\n\t"			\
00db10
+		  "    clgijl %[R_OUTLEN],64,20f\n\t"			\
00db10
+		  "    j 1b\n\t"					\
00db10
+		  "10: \n\t"						\
00db10
+		  /* At least one byte is > 0x7f.			\
00db10
+		     Store the preceding 1-byte chars.  */		\
00db10
+		  "    vlgvb %[R_TMP],%%v17,7\n\t"			\
00db10
+		  "    sllk %[R_TMP2],%[R_TMP],2\n\t" /* Compute highest \
00db10
+						     index to store. */ \
00db10
+		  "    llgfr %[R_TMP3],%[R_TMP2]\n\t"			\
00db10
+		  "    ahi %[R_TMP2],-1\n\t"				\
00db10
+		  "    jl 20f\n\t"					\
00db10
+		  "    vuplhb %%v18,%%v16\n\t"				\
00db10
+		  "    vuplhh %%v20,%%v18\n\t"				\
00db10
+		  "    vstl %%v20,%[R_TMP2],0(%[R_OUT])\n\t"		\
00db10
+		  "    ahi %[R_TMP2],-16\n\t"				\
00db10
+		  "    jl 11f\n\t"					\
00db10
+		  "    vupllh %%v21,%%v18\n\t"				\
00db10
+		  "    vstl %%v21,%[R_TMP2],16(%[R_OUT])\n\t"		\
00db10
+		  "    ahi %[R_TMP2],-16\n\t"				\
00db10
+		  "    jl 11f\n\t"					\
00db10
+		  "    vupllb %%v19,%%v16\n\t"				\
00db10
+		  "    vuplhh %%v22,%%v19\n\t"				\
00db10
+		  "    vstl %%v22,%[R_TMP2],32(%[R_OUT])\n\t"		\
00db10
+		  "    ahi %[R_TMP2],-16\n\t"				\
00db10
+		  "    jl 11f\n\t"					\
00db10
+		  "    vupllh %%v23,%%v19\n\t"				\
00db10
+		  "    vstl %%v23,%[R_TMP2],48(%[R_OUT])\n\t"		\
00db10
+		  "11: \n\t"						\
00db10
+		  /* Update pointers.  */				\
00db10
+		  "    la %[R_IN],0(%[R_TMP],%[R_IN])\n\t"		\
00db10
+		  "    slgr %[R_INLEN],%[R_TMP]\n\t"			\
00db10
+		  "    la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t"		\
00db10
+		  "    slgr %[R_OUTLEN],%[R_TMP3]\n\t"			\
00db10
+		  /* Handle multibyte utf8-char with convert instruction. */ \
00db10
+		  "20: cu14 %[R_OUT],%[R_IN],1\n\t"			\
00db10
+		  "    jo 0b\n\t" /* Try vector implemenation again.  */ \
00db10
+		  "    lochil %[R_RES],%[RES_OUT_FULL]\n\t" /* cc == 1.  */ \
00db10
+		  "    lochih %[R_RES],%[RES_IN_ILL]\n\t" /* cc == 2.  */ \
00db10
+		  ".machine pop"					\
00db10
+		  : /* outputs */ [R_IN] "+a" (pInput)			\
00db10
+		    , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (pOutput)	\
00db10
+		    , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp)	\
00db10
+		    , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3)	\
00db10
+		    , [R_RES] "+d" (result)				\
00db10
+		  : /* inputs */					\
00db10
+		    [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT)		\
00db10
+		    , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT)		\
00db10
+		  : /* clobber list */ "memory", "cc"			\
00db10
+		    ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17")	\
00db10
+		    ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19")	\
00db10
+		    ASM_CLOBBER_VR ("v20") ASM_CLOBBER_VR ("v21")	\
00db10
+		    ASM_CLOBBER_VR ("v22") ASM_CLOBBER_VR ("v30")	\
00db10
+		    ASM_CLOBBER_VR ("v31")				\
00db10
+		  );							\
00db10
+    inptr = pInput;							\
00db10
+    outptr = pOutput;							\
00db10
   }
00db10
+#define BODY_FROM_VX BODY_FROM_HW (HW_FROM_VX)
00db10
 
00db10
-#define UNPACK_BYTES \
00db10
-  {									      \
00db10
-    static const unsigned char inmask[5] = { 0xc0, 0xe0, 0xf0, 0xf8, 0xfc };  \
00db10
-    wint_t wch = state->__value.__wch;					      \
00db10
-    size_t ntotal = state->__count >> 8;				      \
00db10
-									      \
00db10
-    inlen = state->__count & 255;					      \
00db10
-									      \
00db10
-    bytebuf[0] = inmask[ntotal - 2];					      \
00db10
-									      \
00db10
-    do									      \
00db10
-      {									      \
00db10
-	if (--ntotal < inlen)						      \
00db10
-	  bytebuf[ntotal] = 0x80 | (wch & 0x3f);			      \
00db10
-	wch >>= 6;							      \
00db10
-      }									      \
00db10
-    while (ntotal > 1);							      \
00db10
-									      \
00db10
-    bytebuf[0] |= wch;							      \
00db10
-  }
00db10
+/* These definitions apply to the UTF-8 to UTF-32 direction.  The
00db10
+   software implementation for UTF-8 still supports multibyte
00db10
+   characters up to 6 bytes whereas the hardware variant does not.  */
00db10
+#define MIN_NEEDED_INPUT	MIN_NEEDED_FROM
00db10
+#define MAX_NEEDED_INPUT	MAX_NEEDED_FROM
00db10
+#define MIN_NEEDED_OUTPUT	MIN_NEEDED_TO
00db10
+#define LOOPFCT			__from_utf8_loop_c
00db10
 
00db10
-#define CLEAR_STATE \
00db10
-  state->__count = 0
00db10
+#define LOOP_NEED_FLAGS
00db10
 
00db10
+#define STORE_REST		STORE_REST_COMMON
00db10
+#define UNPACK_BYTES		UNPACK_BYTES_COMMON
00db10
+#define CLEAR_STATE		CLEAR_STATE_COMMON
00db10
+#define BODY			BODY_FROM_C
00db10
 #include <iconv/loop.c>
00db10
 
00db10
+
00db10
+/* Generate loop-function with hardware utf-convert instruction.  */
00db10
+#define MIN_NEEDED_INPUT	MIN_NEEDED_FROM
00db10
+#define MAX_NEEDED_INPUT	MAX_NEEDED_FROM
00db10
+#define MIN_NEEDED_OUTPUT	MIN_NEEDED_TO
00db10
+#define LOOPFCT			__from_utf8_loop_etf3eh
00db10
+
00db10
+#define LOOP_NEED_FLAGS
00db10
+
00db10
+#define STORE_REST		STORE_REST_COMMON
00db10
+#define UNPACK_BYTES		UNPACK_BYTES_COMMON
00db10
+#define CLEAR_STATE		CLEAR_STATE_COMMON
00db10
+#define BODY			BODY_FROM_ETF3EH
00db10
+#include <iconv/loop.c>
00db10
+
00db10
+#if defined HAVE_S390_VX_ASM_SUPPORT
00db10
+/* Generate loop-function with hardware vector instructions.  */
00db10
+# define MIN_NEEDED_INPUT	MIN_NEEDED_FROM
00db10
+# define MAX_NEEDED_INPUT	MAX_NEEDED_FROM
00db10
+# define MIN_NEEDED_OUTPUT	MIN_NEEDED_TO
00db10
+# define LOOPFCT		__from_utf8_loop_vx
00db10
+
00db10
+# define LOOP_NEED_FLAGS
00db10
+
00db10
+# define STORE_REST		STORE_REST_COMMON
00db10
+# define UNPACK_BYTES		UNPACK_BYTES_COMMON
00db10
+# define CLEAR_STATE		CLEAR_STATE_COMMON
00db10
+# define BODY			BODY_FROM_VX
00db10
+# include <iconv/loop.c>
00db10
+#endif
00db10
+
00db10
+
00db10
+/* Generate ifunc'ed loop function.  */
00db10
+__typeof(__from_utf8_loop_c)
00db10
+__attribute__ ((ifunc ("__from_utf8_loop_resolver")))
00db10
+__from_utf8_loop;
00db10
+
00db10
+static void *
00db10
+__from_utf8_loop_resolver (unsigned long int dl_hwcap)
00db10
+{
00db10
+#if defined HAVE_S390_VX_ASM_SUPPORT
00db10
+  if (dl_hwcap & HWCAP_S390_VX)
00db10
+    return __from_utf8_loop_vx;
00db10
+  else
00db10
+#endif
00db10
+  if (dl_hwcap & HWCAP_S390_ETF3EH)
00db10
+    return __from_utf8_loop_etf3eh;
00db10
+  else
00db10
+    return __from_utf8_loop_c;
00db10
+}
00db10
+
00db10
+strong_alias (__from_utf8_loop_c_single, __from_utf8_loop_single)
00db10
+
00db10
+
00db10
 /* Conversion from UTF-32 internal/BE to UTF-8.  */
00db10
+#define BODY_TO_HW(ASM)							\
00db10
+  {									\
00db10
+    ASM;								\
00db10
+    if (__glibc_likely (inptr == inend)					\
00db10
+	|| result == __GCONV_FULL_OUTPUT)				\
00db10
+      break;								\
00db10
+    if (inptr + 4 > inend)						\
00db10
+      {									\
00db10
+	result = __GCONV_INCOMPLETE_INPUT;				\
00db10
+	break;								\
00db10
+      }									\
00db10
+    STANDARD_TO_LOOP_ERR_HANDLER (4);					\
00db10
+  }
00db10
+
00db10
+/* The hardware routine uses the S/390 cu41 instruction.  */
00db10
+#define BODY_TO_ETF3EH BODY_TO_HW (HARDWARE_CONVERT ("cu41 %0, %1"))
00db10
+
00db10
+/* The hardware routine uses the S/390 vector and cu41 instructions.  */
00db10
+#define BODY_TO_VX BODY_TO_HW (HW_TO_VX)
00db10
 
00db10
-#define MIN_NEEDED_INPUT	MIN_NEEDED_TO
00db10
-#define MIN_NEEDED_OUTPUT	MIN_NEEDED_FROM
00db10
-#define MAX_NEEDED_OUTPUT	MAX_NEEDED_FROM
00db10
-#define LOOPFCT			TO_LOOP
00db10
 /* The software routine mimics the S/390 cu41 instruction.  */
00db10
-#define BODY							\
00db10
+#define BODY_TO_C						\
00db10
   {								\
00db10
-    if (GLRO (dl_hwcap) & HWCAP_S390_ETF3EH)			\
00db10
-      {								\
00db10
-	HARDWARE_CONVERT ("cu41 %0, %1");			\
00db10
-								\
00db10
-	if (inptr != inend)					\
00db10
-	  {							\
00db10
-	    result = __GCONV_INCOMPLETE_INPUT;			\
00db10
-	    break;						\
00db10
-	  }							\
00db10
-	continue;						\
00db10
-      }								\
00db10
-								\
00db10
     uint32_t wc = *((const uint32_t *) inptr);			\
00db10
 								\
00db10
-    if (__glibc_likely (wc <= 0x7f))					      \
00db10
+    if (__glibc_likely (wc <= 0x7f))				\
00db10
       {								\
00db10
-        /* Single UTF-8 char.  */				\
00db10
-        *outptr = (uint8_t)wc;					\
00db10
+	/* Single UTF-8 char.  */				\
00db10
+	*outptr = (uint8_t)wc;					\
00db10
 	outptr++;						\
00db10
       }								\
00db10
     else if (wc <= 0x7ff)					\
00db10
       {								\
00db10
-        /* Two UTF-8 chars.  */					\
00db10
-        if (__glibc_unlikely (outptr + 2 > outend))			      \
00db10
+	/* Two UTF-8 chars.  */					\
00db10
+	if (__glibc_unlikely (outptr + 2 > outend))		\
00db10
 	  {							\
00db10
 	    /* Overflow in the output buffer.  */		\
00db10
 	    result = __GCONV_FULL_OUTPUT;			\
00db10
 	    break;						\
00db10
 	  }							\
00db10
 								\
00db10
-        outptr[0] = 0xc0;					\
00db10
+	outptr[0] = 0xc0;					\
00db10
 	outptr[0] |= wc >> 6;					\
00db10
 								\
00db10
 	outptr[1] = 0x80;					\
00db10
@@ -459,12 +616,18 @@ gconv_end (struct __gconv_step *data)
00db10
     else if (wc <= 0xffff)					\
00db10
       {								\
00db10
 	/* Three UTF-8 chars.  */				\
00db10
-	if (__glibc_unlikely (outptr + 3 > outend))			      \
00db10
+	if (__glibc_unlikely (outptr + 3 > outend))		\
00db10
 	  {							\
00db10
 	    /* Overflow in the output buffer.  */		\
00db10
 	    result = __GCONV_FULL_OUTPUT;			\
00db10
 	    break;						\
00db10
 	  }							\
00db10
+	if (wc >= 0xd800 && wc < 0xdc00)			\
00db10
+	  {							\
00db10
+	    /* Do not accept UTF-16 surrogates.   */		\
00db10
+	    result = __GCONV_ILLEGAL_INPUT;			\
00db10
+	    STANDARD_TO_LOOP_ERR_HANDLER (4);			\
00db10
+	  }							\
00db10
 	outptr[0] = 0xe0;					\
00db10
 	outptr[0] |= wc >> 12;					\
00db10
 								\
00db10
@@ -479,7 +642,7 @@ gconv_end (struct __gconv_step *data)
00db10
       else if (wc <= 0x10ffff)					\
00db10
 	{							\
00db10
 	  /* Four UTF-8 chars.  */				\
00db10
-	  if (__glibc_unlikely (outptr + 4 > outend))			      \
00db10
+	  if (__glibc_unlikely (outptr + 4 > outend))		\
00db10
 	    {							\
00db10
 	      /* Overflow in the output buffer.  */		\
00db10
 	      result = __GCONV_FULL_OUTPUT;			\
00db10
@@ -505,7 +668,140 @@ gconv_end (struct __gconv_step *data)
00db10
 	}							\
00db10
     inptr += 4;							\
00db10
   }
00db10
+
00db10
+#define HW_TO_VX							\
00db10
+  {									\
00db10
+    register const unsigned char* pInput asm ("8") = inptr;		\
00db10
+    register size_t inlen asm ("9") = inend - inptr;			\
00db10
+    register unsigned char* pOutput asm ("10") = outptr;		\
00db10
+    register size_t outlen asm("11") = outend - outptr;			\
00db10
+    unsigned long tmp, tmp2;						\
00db10
+    asm volatile (".machine push\n\t"					\
00db10
+		  ".machine \"z13\"\n\t"				\
00db10
+		  ".machinemode \"zarch_nohighgprs\"\n\t"		\
00db10
+		  "    vleif %%v20,127,0\n\t"   /* element 0: 127  */	\
00db10
+		  "    vzero %%v21\n\t"					\
00db10
+		  "    vleih %%v21,8192,0\n\t"  /* element 0:   >  */	\
00db10
+		  "    vleih %%v21,-8192,2\n\t" /* element 1: =<>  */	\
00db10
+		  /* Loop which handles UTF-32 chars <=0x7f.  */	\
00db10
+		  "0:  clgijl %[R_INLEN],64,20f\n\t"			\
00db10
+		  "    clgijl %[R_OUTLEN],16,20f\n\t"			\
00db10
+		  "1:  vlm %%v16,%%v19,0(%[R_IN])\n\t"			\
00db10
+		  "    lghi %[R_TMP],0\n\t"				\
00db10
+		  /* Shorten to byte values.  */			\
00db10
+		  "    vpkf %%v23,%%v16,%%v17\n\t"			\
00db10
+		  "    vpkf %%v24,%%v18,%%v19\n\t"			\
00db10
+		  "    vpkh %%v23,%%v23,%%v24\n\t"			\
00db10
+		  /* Checking for values > 0x7f.  */			\
00db10
+		  "    vstrcfs %%v22,%%v16,%%v20,%%v21\n\t"		\
00db10
+		  "    jno 10f\n\t"					\
00db10
+		  "    vstrcfs %%v22,%%v17,%%v20,%%v21\n\t"		\
00db10
+		  "    jno 11f\n\t"					\
00db10
+		  "    vstrcfs %%v22,%%v18,%%v20,%%v21\n\t"		\
00db10
+		  "    jno 12f\n\t"					\
00db10
+		  "    vstrcfs %%v22,%%v19,%%v20,%%v21\n\t"		\
00db10
+		  "    jno 13f\n\t"					\
00db10
+		  /* Store 16bytes to outptr.  */			\
00db10
+		  "    vst %%v23,0(%[R_OUT])\n\t"			\
00db10
+		  "    aghi %[R_INLEN],-64\n\t"				\
00db10
+		  "    aghi %[R_OUTLEN],-16\n\t"			\
00db10
+		  "    la %[R_IN],64(%[R_IN])\n\t"			\
00db10
+		  "    la %[R_OUT],16(%[R_OUT])\n\t"			\
00db10
+		  "    clgijl %[R_INLEN],64,20f\n\t"			\
00db10
+		  "    clgijl %[R_OUTLEN],16,20f\n\t"			\
00db10
+		  "    j 1b\n\t"					\
00db10
+		  /* Found a value > 0x7f.  */				\
00db10
+		  "13: ahi %[R_TMP],4\n\t"				\
00db10
+		  "12: ahi %[R_TMP],4\n\t"				\
00db10
+		  "11: ahi %[R_TMP],4\n\t"				\
00db10
+		  "10: vlgvb %[R_I],%%v22,7\n\t"			\
00db10
+		  "    srlg %[R_I],%[R_I],2\n\t"			\
00db10
+		  "    agr %[R_I],%[R_TMP]\n\t"				\
00db10
+		  "    je 20f\n\t"					\
00db10
+		  /* Store characters before invalid one...  */		\
00db10
+		  "    slgr %[R_OUTLEN],%[R_I]\n\t"			\
00db10
+		  "15: aghi %[R_I],-1\n\t"				\
00db10
+		  "    vstl %%v23,%[R_I],0(%[R_OUT])\n\t"		\
00db10
+		  /* ... and update pointers.  */			\
00db10
+		  "    aghi %[R_I],1\n\t"				\
00db10
+		  "    la %[R_OUT],0(%[R_I],%[R_OUT])\n\t"		\
00db10
+		  "    sllg %[R_I],%[R_I],2\n\t"			\
00db10
+		  "    la %[R_IN],0(%[R_I],%[R_IN])\n\t"		\
00db10
+		  "    slgr %[R_INLEN],%[R_I]\n\t"			\
00db10
+		  /* Handle multibyte utf8-char with convert instruction. */ \
00db10
+		  "20: cu41 %[R_OUT],%[R_IN]\n\t"			\
00db10
+		  "    jo 0b\n\t" /* Try vector implemenation again.  */ \
00db10
+		  "    lochil %[R_RES],%[RES_OUT_FULL]\n\t" /* cc == 1.  */ \
00db10
+		  "    lochih %[R_RES],%[RES_IN_ILL]\n\t" /* cc == 2.  */ \
00db10
+		  ".machine pop"					\
00db10
+		  : /* outputs */ [R_IN] "+a" (pInput)			\
00db10
+		    , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (pOutput)	\
00db10
+		    , [R_OUTLEN] "+d" (outlen), [R_TMP] "=d" (tmp)	\
00db10
+		    , [R_I] "=a" (tmp2)					\
00db10
+		    , [R_RES] "+d" (result)				\
00db10
+		  : /* inputs */					\
00db10
+		    [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT)		\
00db10
+		    , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT)		\
00db10
+		  : /* clobber list */ "memory", "cc"			\
00db10
+		    ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17")	\
00db10
+		    ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19")	\
00db10
+		    ASM_CLOBBER_VR ("v20") ASM_CLOBBER_VR ("v21")	\
00db10
+		    ASM_CLOBBER_VR ("v22") ASM_CLOBBER_VR ("v23")	\
00db10
+		    ASM_CLOBBER_VR ("v24")				\
00db10
+		  );							\
00db10
+    inptr = pInput;							\
00db10
+    outptr = pOutput;							\
00db10
+  }
00db10
+
00db10
+/* Generate loop-function with software routing.  */
00db10
+#define MIN_NEEDED_INPUT	MIN_NEEDED_TO
00db10
+#define MIN_NEEDED_OUTPUT	MIN_NEEDED_FROM
00db10
+#define MAX_NEEDED_OUTPUT	MAX_NEEDED_FROM
00db10
+#define LOOPFCT			__to_utf8_loop_c
00db10
+#define BODY			BODY_TO_C
00db10
+#define LOOP_NEED_FLAGS
00db10
+#include <iconv/loop.c>
00db10
+
00db10
+/* Generate loop-function with hardware utf-convert instruction.  */
00db10
+#define MIN_NEEDED_INPUT	MIN_NEEDED_TO
00db10
+#define MIN_NEEDED_OUTPUT	MIN_NEEDED_FROM
00db10
+#define MAX_NEEDED_OUTPUT	MAX_NEEDED_FROM
00db10
+#define LOOPFCT			__to_utf8_loop_etf3eh
00db10
 #define LOOP_NEED_FLAGS
00db10
+#define BODY			BODY_TO_ETF3EH
00db10
 #include <iconv/loop.c>
00db10
 
00db10
+#if defined HAVE_S390_VX_ASM_SUPPORT
00db10
+/* Generate loop-function with hardware vector and utf-convert instructions.  */
00db10
+# define MIN_NEEDED_INPUT	MIN_NEEDED_TO
00db10
+# define MIN_NEEDED_OUTPUT	MIN_NEEDED_FROM
00db10
+# define MAX_NEEDED_OUTPUT	MAX_NEEDED_FROM
00db10
+# define LOOPFCT		__to_utf8_loop_vx
00db10
+# define BODY			BODY_TO_VX
00db10
+# define LOOP_NEED_FLAGS
00db10
+# include <iconv/loop.c>
00db10
+#endif
00db10
+
00db10
+/* Generate ifunc'ed loop function.  */
00db10
+__typeof(__to_utf8_loop_c)
00db10
+__attribute__ ((ifunc ("__to_utf8_loop_resolver")))
00db10
+__to_utf8_loop;
00db10
+
00db10
+static void *
00db10
+__to_utf8_loop_resolver (unsigned long int dl_hwcap)
00db10
+{
00db10
+#if defined HAVE_S390_VX_ASM_SUPPORT
00db10
+  if (dl_hwcap & HWCAP_S390_VX)
00db10
+    return __to_utf8_loop_vx;
00db10
+  else
00db10
+#endif
00db10
+  if (dl_hwcap & HWCAP_S390_ETF3EH)
00db10
+    return __to_utf8_loop_etf3eh;
00db10
+  else
00db10
+    return __to_utf8_loop_c;
00db10
+}
00db10
+
00db10
+strong_alias (__to_utf8_loop_c_single, __to_utf8_loop_single)
00db10
+
00db10
+
00db10
 #include <iconv/skeleton.c>
00db10
-- 
00db10
1.8.3.1
00db10