1 /* Conversion between UTF-8 and UTF-16 - s390 version.
2 
3    This module uses the Z9-109 variants of the Convert Unicode
4    instructions.
5    Copyright (C) 1997-2022 Free Software Foundation, Inc.
6 
7    This is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11 
12    This is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16 
17    You should have received a copy of the GNU Lesser General Public
18    License along with the GNU C Library; if not, see
19    <https://www.gnu.org/licenses/>.  */
20 
21 #include <dlfcn.h>
22 #include <stdint.h>
23 #include <unistd.h>
24 #include <gconv.h>
25 #include <string.h>
26 
27 /* Select which versions should be defined depending on support
28    for multiarch, vector and used minimum architecture level.  */
29 #ifdef HAVE_S390_MIN_Z196_ZARCH_ASM_SUPPORT
30 # define HAVE_FROM_C		0
31 # define FROM_LOOP_DEFAULT	FROM_LOOP_CU
32 #else
33 # define HAVE_FROM_C		1
34 # define FROM_LOOP_DEFAULT	FROM_LOOP_C
35 #endif
36 
37 #define HAVE_TO_C		1
38 #define TO_LOOP_DEFAULT		TO_LOOP_C
39 
40 #if defined HAVE_S390_MIN_Z196_ZARCH_ASM_SUPPORT || defined USE_MULTIARCH
41 # define HAVE_FROM_CU		1
42 #else
43 # define HAVE_FROM_CU		0
44 #endif
45 
46 #if defined HAVE_S390_VX_ASM_SUPPORT && defined USE_MULTIARCH
47 # define HAVE_FROM_VX		1
48 # define HAVE_TO_VX		1
49 # define HAVE_TO_VX_CU		1
50 #else
51 # define HAVE_FROM_VX		0
52 # define HAVE_TO_VX		0
53 # define HAVE_TO_VX_CU		0
54 #endif
55 
56 #if defined HAVE_S390_VX_GCC_SUPPORT
57 # define ASM_CLOBBER_VR(NR) , NR
58 #else
59 # define ASM_CLOBBER_VR(NR)
60 #endif
61 
62 #if defined __s390x__
63 # define CONVERT_32BIT_SIZE_T(REG)
64 #else
65 # define CONVERT_32BIT_SIZE_T(REG) "llgfr %" #REG ",%" #REG "\n\t"
66 #endif
67 
68 /* Defines for skeleton.c.  */
69 #define DEFINE_INIT		0
70 #define DEFINE_FINI		0
71 #define MIN_NEEDED_FROM		1
72 #define MAX_NEEDED_FROM		4
73 #define MIN_NEEDED_TO		2
74 #define MAX_NEEDED_TO		4
75 #define FROM_LOOP		FROM_LOOP_DEFAULT
76 #define TO_LOOP			TO_LOOP_DEFAULT
77 #define FROM_DIRECTION		(dir == from_utf8)
78 #define ONE_DIRECTION           0
79 
80 
81 /* UTF-16 big endian byte order mark.  */
82 #define BOM_UTF16	0xfeff
83 
84 /* Direction of the transformation.  */
85 enum direction
86 {
87   illegal_dir,
88   to_utf8,
89   from_utf8
90 };
91 
92 struct utf8_data
93 {
94   enum direction dir;
95   int emit_bom;
96 };
97 
98 
99 extern int gconv_init (struct __gconv_step *step);
100 int
gconv_init(struct __gconv_step * step)101 gconv_init (struct __gconv_step *step)
102 {
103   /* Determine which direction.  */
104   struct utf8_data *new_data;
105   enum direction dir = illegal_dir;
106   int emit_bom;
107   int result;
108 
109   emit_bom = (__strcasecmp (step->__to_name, "UTF-16//") == 0);
110 
111   if (__strcasecmp (step->__from_name, "ISO-10646/UTF8/") == 0
112       && (__strcasecmp (step->__to_name, "UTF-16//") == 0
113 	  || __strcasecmp (step->__to_name, "UTF-16BE//") == 0))
114     {
115       dir = from_utf8;
116     }
117   else if (__strcasecmp (step->__from_name, "UTF-16BE//") == 0
118 	   && __strcasecmp (step->__to_name, "ISO-10646/UTF8/") == 0)
119     {
120       dir = to_utf8;
121     }
122 
123   result = __GCONV_NOCONV;
124   if (dir != illegal_dir)
125     {
126       new_data = (struct utf8_data *) malloc (sizeof (struct utf8_data));
127 
128       result = __GCONV_NOMEM;
129       if (new_data != NULL)
130 	{
131 	  new_data->dir = dir;
132 	  new_data->emit_bom = emit_bom;
133 	  step->__data = new_data;
134 
135 	  if (dir == from_utf8)
136 	    {
137 	      step->__min_needed_from = MIN_NEEDED_FROM;
138 	      step->__max_needed_from = MIN_NEEDED_FROM;
139 	      step->__min_needed_to = MIN_NEEDED_TO;
140 	      step->__max_needed_to = MIN_NEEDED_TO;
141 	    }
142 	  else
143 	    {
144 	      step->__min_needed_from = MIN_NEEDED_TO;
145 	      step->__max_needed_from = MIN_NEEDED_TO;
146 	      step->__min_needed_to = MIN_NEEDED_FROM;
147 	      step->__max_needed_to = MIN_NEEDED_FROM;
148 	    }
149 
150 	  step->__stateful = 0;
151 
152 	  result = __GCONV_OK;
153 	}
154     }
155 
156   return result;
157 }
158 
159 
160 extern void gconv_end (struct __gconv_step *data);
161 void
gconv_end(struct __gconv_step * data)162 gconv_end (struct __gconv_step *data)
163 {
164   free (data->__data);
165 }
166 
167 /* The macro for the hardware loop.  This is used for both
168    directions.  */
169 #define HARDWARE_CONVERT(INSTRUCTION)					\
170   {									\
171     register const unsigned char* pInput __asm__ ("8") = inptr;		\
172     register size_t inlen __asm__ ("9") = inend - inptr;		\
173     register unsigned char* pOutput __asm__ ("10") = outptr;		\
174     register size_t outlen __asm__("11") = outend - outptr;		\
175     unsigned long cc = 0;						\
176 									\
177     __asm__ __volatile__ (".machine push       \n\t"			\
178 			  ".machine \"z9-109\" \n\t"			\
179 			  ".machinemode \"zarch_nohighgprs\"\n\t"	\
180 			  "0: " INSTRUCTION "  \n\t"			\
181 			  ".machine pop        \n\t"			\
182 			  "   jo     0b        \n\t"			\
183 			  "   ipm    %2        \n"			\
184 			  : "+a" (pOutput), "+a" (pInput), "+d" (cc),	\
185 			    "+d" (outlen), "+d" (inlen)			\
186 			  :						\
187 			  : "cc", "memory");				\
188 									\
189     inptr = pInput;							\
190     outptr = pOutput;							\
191     cc >>= 28;								\
192 									\
193     if (cc == 1)							\
194       {									\
195 	result = __GCONV_FULL_OUTPUT;					\
196       }									\
197     else if (cc == 2)							\
198       {									\
199 	result = __GCONV_ILLEGAL_INPUT;					\
200       }									\
201   }
202 
203 #define PREPARE_LOOP							\
204   enum direction dir = ((struct utf8_data *) step->__data)->dir;	\
205   int emit_bom = ((struct utf8_data *) step->__data)->emit_bom;		\
206 									\
207   if (emit_bom && !data->__internal_use					\
208       && data->__invocation_counter == 0)				\
209     {									\
210       /* Emit the UTF-16 Byte Order Mark.  */				\
211       if (__glibc_unlikely (outbuf + 2 > outend))			\
212 	return __GCONV_FULL_OUTPUT;					\
213 									\
214       put16u (outbuf, BOM_UTF16);					\
215       outbuf += 2;							\
216     }
217 
218 /* Conversion function from UTF-8 to UTF-16.  */
219 #define BODY_FROM_HW(ASM)						\
220   {									\
221     ASM;								\
222     if (__glibc_likely (inptr == inend)					\
223 	|| result == __GCONV_FULL_OUTPUT)				\
224       break;								\
225 									\
226     int i;								\
227     for (i = 1; inptr + i < inend && i < 5; ++i)			\
228       if ((inptr[i] & 0xc0) != 0x80)					\
229 	break;								\
230 									\
231     if (__glibc_likely (inptr + i == inend				\
232 			&& result == __GCONV_EMPTY_INPUT))		\
233       {									\
234 	result = __GCONV_INCOMPLETE_INPUT;				\
235 	break;								\
236       }									\
237     STANDARD_FROM_LOOP_ERR_HANDLER (i);					\
238   }
239 
240 #if HAVE_FROM_VX == 1
241 # define HW_FROM_VX							\
242   {									\
243     register const unsigned char* pInput asm ("8") = inptr;		\
244     register size_t inlen asm ("9") = inend - inptr;			\
245     register unsigned char* pOutput asm ("10") = outptr;		\
246     register size_t outlen asm("11") = outend - outptr;			\
247     unsigned long tmp, tmp2, tmp3;					\
248     asm volatile (".machine push\n\t"					\
249 		  ".machine \"z13\"\n\t"				\
250 		  ".machinemode \"zarch_nohighgprs\"\n\t"		\
251 		  "    vrepib %%v30,0x7f\n\t" /* For compare > 0x7f.  */ \
252 		  "    vrepib %%v31,0x20\n\t"				\
253 		  CONVERT_32BIT_SIZE_T ([R_INLEN])			\
254 		  CONVERT_32BIT_SIZE_T ([R_OUTLEN])			\
255 		  /* Loop which handles UTF-8 chars <=0x7f.  */		\
256 		  "0:  clgijl %[R_INLEN],16,20f\n\t"			\
257 		  "    clgijl %[R_OUTLEN],32,20f\n\t"			\
258 		  "1:  vl %%v16,0(%[R_IN])\n\t"				\
259 		  "    vstrcbs %%v17,%%v16,%%v30,%%v31\n\t"		\
260 		  "    jno 10f\n\t" /* Jump away if not all bytes are 1byte \
261 				       UTF8 chars.  */			\
262 		  /* Enlarge to UTF-16.  */				\
263 		  "    vuplhb %%v18,%%v16\n\t"				\
264 		  "    la %[R_IN],16(%[R_IN])\n\t"			\
265 		  "    vupllb %%v19,%%v16\n\t"				\
266 		  "    aghi %[R_INLEN],-16\n\t"				\
267 		  /* Store 32 bytes to buf_out.  */			\
268 		  "    vstm %%v18,%%v19,0(%[R_OUT])\n\t"		\
269 		  "    aghi %[R_OUTLEN],-32\n\t"			\
270 		  "    la %[R_OUT],32(%[R_OUT])\n\t"			\
271 		  "    clgijl %[R_INLEN],16,20f\n\t"			\
272 		  "    clgijl %[R_OUTLEN],32,20f\n\t"			\
273 		  "    j 1b\n\t"					\
274 		  "10:\n\t"						\
275 		  /* At least one byte is > 0x7f.			\
276 		     Store the preceding 1-byte chars.  */		\
277 		  "    vlgvb %[R_TMP],%%v17,7\n\t"			\
278 		  "    sllk %[R_TMP2],%[R_TMP],1\n\t" /* Compute highest \
279 							 index to store. */ \
280 		  "    llgfr %[R_TMP3],%[R_TMP2]\n\t"			\
281 		  "    ahi %[R_TMP2],-1\n\t"				\
282 		  "    jl 20f\n\t"					\
283 		  "    vuplhb %%v18,%%v16\n\t"				\
284 		  "    vstl %%v18,%[R_TMP2],0(%[R_OUT])\n\t"		\
285 		  "    ahi %[R_TMP2],-16\n\t"				\
286 		  "    jl 11f\n\t"					\
287 		  "    vupllb %%v19,%%v16\n\t"				\
288 		  "    vstl %%v19,%[R_TMP2],16(%[R_OUT])\n\t"		\
289 		  "11: \n\t" /* Update pointers.  */			\
290 		  "    la %[R_IN],0(%[R_TMP],%[R_IN])\n\t"		\
291 		  "    slgr %[R_INLEN],%[R_TMP]\n\t"			\
292 		  "    la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t"		\
293 		  "    slgr %[R_OUTLEN],%[R_TMP3]\n\t"			\
294 		  /* Handle multibyte utf8-char with convert instruction. */ \
295 		  "20: cu12 %[R_OUT],%[R_IN],1\n\t"			\
296 		  "    jo 0b\n\t" /* Try vector implemenation again.  */ \
297 		  "    lochil %[R_RES],%[RES_OUT_FULL]\n\t" /* cc == 1.  */ \
298 		  "    lochih %[R_RES],%[RES_IN_ILL]\n\t" /* cc == 2.  */ \
299 		  ".machine pop"					\
300 		  : /* outputs */ [R_IN] "+a" (pInput)			\
301 		    , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (pOutput)	\
302 		    , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp)	\
303 		    , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3)	\
304 		    , [R_RES] "+d" (result)				\
305 		  : /* inputs */					\
306 		    [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT)		\
307 		    , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT)		\
308 		  : /* clobber list */ "memory", "cc"			\
309 		    ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17")	\
310 		    ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19")	\
311 		    ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31")	\
312 		  );							\
313     inptr = pInput;							\
314     outptr = pOutput;							\
315   }
316 # define BODY_FROM_VX BODY_FROM_HW (HW_FROM_VX)
317 
318 /* Generate loop-function with hardware vector and utf-convert instructions.  */
319 # define MIN_NEEDED_INPUT	MIN_NEEDED_FROM
320 # define MAX_NEEDED_INPUT	MAX_NEEDED_FROM
321 # define MIN_NEEDED_OUTPUT	MIN_NEEDED_TO
322 # define MAX_NEEDED_OUTPUT	MAX_NEEDED_TO
323 # define FROM_LOOP_VX		__from_utf8_loop_vx
324 # define LOOPFCT		FROM_LOOP_VX
325 # define LOOP_NEED_FLAGS
326 # define BODY			BODY_FROM_VX
327 # include <iconv/loop.c>
328 #else
329 # define FROM_LOOP_VX		NULL
330 #endif /* HAVE_FROM_VX != 1  */
331 
332 #if HAVE_FROM_CU == 1
333 # define BODY_FROM_ETF3EH BODY_FROM_HW (HARDWARE_CONVERT ("cu12 %0, %1, 1"))
334 
335 /* Generate loop-function with hardware utf-convert instruction.  */
336 # define MIN_NEEDED_INPUT	MIN_NEEDED_FROM
337 # define MAX_NEEDED_INPUT	MAX_NEEDED_FROM
338 # define MIN_NEEDED_OUTPUT	MIN_NEEDED_TO
339 # define MAX_NEEDED_OUTPUT	MAX_NEEDED_TO
340 # define FROM_LOOP_CU		__from_utf8_loop_etf3eh
341 # define LOOPFCT		FROM_LOOP_CU
342 # define LOOP_NEED_FLAGS
343 # define BODY			BODY_FROM_ETF3EH
344 # include <iconv/loop.c>
345 #else
346 # define FROM_LOOP_CU		NULL
347 #endif /* HAVE_FROM_CU != 1  */
348 
349 #if HAVE_FROM_C == 1
350 /* The software implementation is based on the code in gconv_simple.c.  */
351 # define BODY_FROM_C							\
352   {									\
353     /* Next input byte.  */						\
354     uint16_t ch = *inptr;						\
355 									\
356     if (__glibc_likely (ch < 0x80))					\
357       {									\
358 	/* One byte sequence.  */					\
359 	++inptr;							\
360       }									\
361     else								\
362       {									\
363 	uint_fast32_t cnt;						\
364 	uint_fast32_t i;						\
365 									\
366 	if (ch >= 0xc2 && ch < 0xe0)					\
367 	  {								\
368 	    /* We expect two bytes.  The first byte cannot be 0xc0	\
369 	       or 0xc1, otherwise the wide character could have been	\
370 	       represented using a single byte.  */			\
371 	    cnt = 2;							\
372 	    ch &= 0x1f;							\
373 	  }								\
374 	else if (__glibc_likely ((ch & 0xf0) == 0xe0))			\
375 	  {								\
376 	    /* We expect three bytes.  */				\
377 	    cnt = 3;							\
378 	    ch &= 0x0f;							\
379 	  }								\
380 	else if (__glibc_likely ((ch & 0xf8) == 0xf0))			\
381 	  {								\
382 	    /* We expect four bytes.  */				\
383 	    cnt = 4;							\
384 	    ch &= 0x07;							\
385 	  }								\
386 	else								\
387 	  {								\
388 	    /* Search the end of this ill-formed UTF-8 character.  This	\
389 	       is the next byte with (x & 0xc0) != 0x80.  */		\
390 	    i = 0;							\
391 	    do								\
392 	      ++i;							\
393 	    while (inptr + i < inend					\
394 		   && (*(inptr + i) & 0xc0) == 0x80			\
395 		   && i < 5);						\
396 									\
397 	  errout:							\
398 	    STANDARD_FROM_LOOP_ERR_HANDLER (i);				\
399 	  }								\
400 									\
401 	if (__glibc_unlikely (inptr + cnt > inend))			\
402 	  {								\
403 	    /* We don't have enough input.  But before we report	\
404 	       that check that all the bytes are correct.  */		\
405 	    for (i = 1; inptr + i < inend; ++i)				\
406 	      if ((inptr[i] & 0xc0) != 0x80)				\
407 		break;							\
408 									\
409 	    if (__glibc_likely (inptr + i == inend))			\
410 	      {								\
411 		result = __GCONV_INCOMPLETE_INPUT;			\
412 		break;							\
413 	      }								\
414 									\
415 	    goto errout;						\
416 	  }								\
417 									\
418 	if (cnt == 4)							\
419 	  {								\
420 	    /* For 4 byte UTF-8 chars two UTF-16 chars (high and	\
421 	       low) are needed.  */					\
422 	    uint16_t zabcd, high, low;					\
423 									\
424 	    if (__glibc_unlikely (outptr + 4 > outend))			\
425 	      {								\
426 		/* Overflow in the output buffer.  */			\
427 		result = __GCONV_FULL_OUTPUT;				\
428 		break;							\
429 	      }								\
430 									\
431 	    /* Check if tail-bytes >= 0x80, < 0xc0.  */			\
432 	    for (i = 1; i < cnt; ++i)					\
433 	      {								\
434 		if ((inptr[i] & 0xc0) != 0x80)				\
435 		  /* This is an illegal encoding.  */			\
436 		  goto errout;						\
437 	      }								\
438 									\
439 	    /* See Principles of Operations cu12.  */			\
440 	    zabcd = (((inptr[0] & 0x7) << 2)				\
441 		     | ((inptr[1] & 0x30) >> 4)) - 1;			\
442 									\
443 	    /* z-bit must be zero after subtracting 1.  */		\
444 	    if (zabcd & 0x10)						\
445 	      STANDARD_FROM_LOOP_ERR_HANDLER (4)			\
446 									\
447 	    high = (uint16_t)(0xd8 << 8);       /* high surrogate id */ \
448 	    high |= zabcd << 6;                         /* abcd bits */	\
449 	    high |= (inptr[1] & 0xf) << 2;              /* efgh bits */	\
450 	    high |= (inptr[2] & 0x30) >> 4;               /* ij bits */	\
451 									\
452 	    low = (uint16_t)(0xdc << 8);         /* low surrogate id */ \
453 	    low |= ((uint16_t)inptr[2] & 0xc) << 6;       /* kl bits */	\
454 	    low |= (inptr[2] & 0x3) << 6;                 /* mn bits */	\
455 	    low |= inptr[3] & 0x3f;                   /* opqrst bits */	\
456 									\
457 	    put16 (outptr, high);					\
458 	    outptr += 2;						\
459 	    put16 (outptr, low);					\
460 	    outptr += 2;						\
461 	    inptr += 4;							\
462 	    continue;							\
463 	  }								\
464 	else								\
465 	  {								\
466 	    /* Read the possible remaining bytes.  */			\
467 	    for (i = 1; i < cnt; ++i)					\
468 	      {								\
469 		uint16_t byte = inptr[i];				\
470 									\
471 		if ((byte & 0xc0) != 0x80)				\
472 		  /* This is an illegal encoding.  */			\
473 		  break;						\
474 									\
475 		ch <<= 6;						\
476 		ch |= byte & 0x3f;					\
477 	      }								\
478 									\
479 	    /* If i < cnt, some trail byte was not >= 0x80, < 0xc0.	\
480 	       If cnt > 2 and ch < 2^(5*cnt-4), the wide character ch could \
481 	       have been represented with fewer than cnt bytes.  */	\
482 	    if (i < cnt || (cnt > 2 && (ch >> (5 * cnt - 4)) == 0)	\
483 		/* Do not accept UTF-16 surrogates.  */			\
484 		|| (ch >= 0xd800 && ch <= 0xdfff))			\
485 	      {								\
486 		/* This is an illegal encoding.  */			\
487 		goto errout;						\
488 	      }								\
489 									\
490 	    inptr += cnt;						\
491 	  }								\
492       }									\
493     /* Now adjust the pointers and store the result.  */		\
494     *((uint16_t *) outptr) = ch;					\
495     outptr += sizeof (uint16_t);					\
496   }
497 
498 /* Generate loop-function with software implementation.  */
499 # define MIN_NEEDED_INPUT	MIN_NEEDED_FROM
500 # define MAX_NEEDED_INPUT	MAX_NEEDED_FROM
501 # define MIN_NEEDED_OUTPUT	MIN_NEEDED_TO
502 # define MAX_NEEDED_OUTPUT	MAX_NEEDED_TO
503 # define FROM_LOOP_C		__from_utf8_loop_c
504 # define LOOPFCT		FROM_LOOP_C
505 # define LOOP_NEED_FLAGS
506 # define BODY			BODY_FROM_C
507 # include <iconv/loop.c>
508 #else
509 # define FROM_LOOP_C		NULL
510 #endif /* HAVE_FROM_C != 1  */
511 
512 /* Conversion from UTF-16 to UTF-8.  */
513 
514 #if HAVE_TO_C == 1
515 /* The software routine is based on the functionality of the S/390
516    hardware instruction (cu21) as described in the Principles of
517    Operation.  */
518 # define BODY_TO_C							\
519   {									\
520     uint16_t c = get16 (inptr);						\
521 									\
522     if (__glibc_likely (c <= 0x007f))					\
523       {									\
524 	/* Single byte UTF-8 char.  */					\
525 	*outptr = c & 0xff;						\
526 	outptr++;							\
527       }									\
528     else if (c >= 0x0080 && c <= 0x07ff)				\
529       {									\
530 	/* Two byte UTF-8 char.  */					\
531 									\
532 	if (__glibc_unlikely (outptr + 2 > outend))			\
533 	  {								\
534 	    /* Overflow in the output buffer.  */			\
535 	    result = __GCONV_FULL_OUTPUT;				\
536 	    break;							\
537 	  }								\
538 									\
539 	outptr[0] = 0xc0;						\
540 	outptr[0] |= c >> 6;						\
541 									\
542 	outptr[1] = 0x80;						\
543 	outptr[1] |= c & 0x3f;						\
544 									\
545 	outptr += 2;							\
546       }									\
547     else if ((c >= 0x0800 && c <= 0xd7ff) || c > 0xdfff)		\
548       {									\
549 	/* Three byte UTF-8 char.  */					\
550 									\
551 	if (__glibc_unlikely (outptr + 3 > outend))			\
552 	  {								\
553 	    /* Overflow in the output buffer.  */			\
554 	    result = __GCONV_FULL_OUTPUT;				\
555 	    break;							\
556 	  }								\
557 	outptr[0] = 0xe0;						\
558 	outptr[0] |= c >> 12;						\
559 									\
560 	outptr[1] = 0x80;						\
561 	outptr[1] |= (c >> 6) & 0x3f;					\
562 									\
563 	outptr[2] = 0x80;						\
564 	outptr[2] |= c & 0x3f;						\
565 									\
566 	outptr += 3;							\
567       }									\
568     else if (c >= 0xd800 && c <= 0xdbff)				\
569       {									\
570 	/* Four byte UTF-8 char.  */					\
571 	uint16_t low, uvwxy;						\
572 									\
573 	if (__glibc_unlikely (outptr + 4 > outend))			\
574 	  {								\
575 	    /* Overflow in the output buffer.  */			\
576 	    result = __GCONV_FULL_OUTPUT;				\
577 	    break;							\
578 	  }								\
579 	if (__glibc_unlikely (inptr + 4 > inend))			\
580 	  {								\
581 	    result = __GCONV_INCOMPLETE_INPUT;				\
582 	    break;							\
583 	  }								\
584 									\
585 	inptr += 2;							\
586 	low = get16 (inptr);						\
587 									\
588 	if ((low & 0xfc00) != 0xdc00)					\
589 	  {								\
590 	    inptr -= 2;							\
591 	    STANDARD_TO_LOOP_ERR_HANDLER (2);				\
592 	  }								\
593 	uvwxy = ((c >> 6) & 0xf) + 1;					\
594 	outptr[0] = 0xf0;						\
595 	outptr[0] |= uvwxy >> 2;					\
596 									\
597 	outptr[1] = 0x80;						\
598 	outptr[1] |= (uvwxy << 4) & 0x30;				\
599 	outptr[1] |= (c >> 2) & 0x0f;					\
600 									\
601 	outptr[2] = 0x80;						\
602 	outptr[2] |= (c & 0x03) << 4;					\
603 	outptr[2] |= (low >> 6) & 0x0f;					\
604 									\
605 	outptr[3] = 0x80;						\
606 	outptr[3] |= low & 0x3f;					\
607 									\
608 	outptr += 4;							\
609       }									\
610     else								\
611       {									\
612 	STANDARD_TO_LOOP_ERR_HANDLER (2);				\
613       }									\
614     inptr += 2;								\
615   }
616 
617 /* Generate loop-function with software implementation.  */
618 # define MIN_NEEDED_INPUT	MIN_NEEDED_TO
619 # define MAX_NEEDED_INPUT	MAX_NEEDED_TO
620 # define MIN_NEEDED_OUTPUT	MIN_NEEDED_FROM
621 # define MAX_NEEDED_OUTPUT	MAX_NEEDED_FROM
622 # define TO_LOOP_C		__to_utf8_loop_c
623 # define LOOPFCT		TO_LOOP_C
624 # define BODY                   BODY_TO_C
625 # define LOOP_NEED_FLAGS
626 # include <iconv/loop.c>
627 #else
628 # define TO_LOOP_C		NULL
629 #endif /* HAVE_TO_C != 1  */
630 
631 #if HAVE_TO_VX == 1
632 # define BODY_TO_VX							\
633   {									\
634     size_t inlen  = inend - inptr;					\
635     size_t outlen  = outend - outptr;					\
636     unsigned long tmp, tmp2, tmp3;					\
637     asm volatile (".machine push\n\t"					\
638 		  ".machine \"z13\"\n\t"				\
639 		  ".machinemode \"zarch_nohighgprs\"\n\t"		\
640 		  /* Setup to check for values <= 0x7f.  */		\
641 		  "    larl %[R_TMP],9f\n\t"				\
642 		  "    vlm %%v30,%%v31,0(%[R_TMP])\n\t"			\
643 		  CONVERT_32BIT_SIZE_T ([R_INLEN])			\
644 		  CONVERT_32BIT_SIZE_T ([R_OUTLEN])			\
645 		  /* Loop which handles UTF-16 chars <=0x7f.  */	\
646 		  "0:  clgijl %[R_INLEN],32,2f\n\t"			\
647 		  "    clgijl %[R_OUTLEN],16,2f\n\t"			\
648 		  "1:  vlm %%v16,%%v17,0(%[R_IN])\n\t"			\
649 		  "    lghi %[R_TMP2],0\n\t"				\
650 		  /* Check for > 1byte UTF-8 chars.  */			\
651 		  "    vstrchs %%v19,%%v16,%%v30,%%v31\n\t"		\
652 		  "    jno 10f\n\t" /* Jump away if not all bytes are 1byte \
653 				       UTF8 chars.  */			\
654 		  "    vstrchs %%v19,%%v17,%%v30,%%v31\n\t"		\
655 		  "    jno 11f\n\t" /* Jump away if not all bytes are 1byte \
656 				       UTF8 chars.  */			\
657 		  /* Shorten to UTF-8.  */				\
658 		  "    vpkh %%v18,%%v16,%%v17\n\t"			\
659 		  "    la %[R_IN],32(%[R_IN])\n\t"			\
660 		  "    aghi %[R_INLEN],-32\n\t"				\
661 		  /* Store 16 bytes to buf_out.  */			\
662 		  "    vst %%v18,0(%[R_OUT])\n\t"			\
663 		  "    aghi %[R_OUTLEN],-16\n\t"			\
664 		  "    la %[R_OUT],16(%[R_OUT])\n\t"			\
665 		  "    clgijl %[R_INLEN],32,2f\n\t"			\
666 		  "    clgijl %[R_OUTLEN],16,2f\n\t"			\
667 		  "    j 1b\n\t"					\
668 		  /* Setup to check for ch > 0x7f. (v30, v31)  */	\
669 		  "9:  .short 0x7f,0x7f,0x0,0x0,0x0,0x0,0x0,0x0\n\t"	\
670 		  "    .short 0x2000,0x2000,0x0,0x0,0x0,0x0,0x0,0x0\n\t" \
671 		  /* At least one byte is > 0x7f.			\
672 		     Store the preceding 1-byte chars.  */		\
673 		  "11: lghi %[R_TMP2],16\n\t" /* match was found in v17.  */ \
674 		  "10:\n\t"						\
675 		  "    vlgvb %[R_TMP],%%v19,7\n\t"			\
676 		  /* Shorten to UTF-8.  */				\
677 		  "    vpkh %%v18,%%v16,%%v17\n\t"			\
678 		  "    ar %[R_TMP],%[R_TMP2]\n\t" /* Number of in bytes.  */ \
679 		  "    srlg %[R_TMP3],%[R_TMP],1\n\t" /* Number of out bytes.  */ \
680 		  "    ahik %[R_TMP2],%[R_TMP3],-1\n\t" /* Highest index to store.  */ \
681 		  "    jl 13f\n\t"					\
682 		  "    vstl %%v18,%[R_TMP2],0(%[R_OUT])\n\t"		\
683 		  /* Update pointers.  */				\
684 		  "    la %[R_IN],0(%[R_TMP],%[R_IN])\n\t"		\
685 		  "    slgr %[R_INLEN],%[R_TMP]\n\t"			\
686 		  "    la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t"		\
687 		  "    slgr %[R_OUTLEN],%[R_TMP3]\n\t"			\
688 		  "13: \n\t"						\
689 		  /* Calculate remaining uint16_t values in loaded vrs.  */ \
690 		  "    lghi %[R_TMP2],16\n\t"				\
691 		  "    slgr %[R_TMP2],%[R_TMP3]\n\t"			\
692 		  "    llh %[R_TMP],0(%[R_IN])\n\t"			\
693 		  "    aghi %[R_INLEN],-2\n\t"				\
694 		  "    j 22f\n\t"					\
695 		  /* Handle remaining bytes.  */			\
696 		  "2:  \n\t"						\
697 		  /* Zero, one or more bytes available?  */		\
698 		  "    clgfi %[R_INLEN],1\n\t"				\
699 		  "    locghie %[R_RES],%[RES_IN_FULL]\n\t" /* Only one byte.  */ \
700 		  "    jle 99f\n\t" /* End if less than two bytes.  */	\
701 		  /* Calculate remaining uint16_t values in inptr.  */	\
702 		  "    srlg %[R_TMP2],%[R_INLEN],1\n\t"			\
703 		  /* Handle multibyte utf8-char. */			\
704 		  "20: llh %[R_TMP],0(%[R_IN])\n\t"			\
705 		  "    aghi %[R_INLEN],-2\n\t"				\
706 		  /* Test if ch is 1-byte UTF-8 char.  */		\
707 		  "21: clijh %[R_TMP],0x7f,22f\n\t"			\
708 		  /* Handle 1-byte UTF-8 char.  */			\
709 		  "31: slgfi %[R_OUTLEN],1\n\t"				\
710 		  "    jl 90f \n\t"					\
711 		  "    stc %[R_TMP],0(%[R_OUT])\n\t"			\
712 		  "    la %[R_IN],2(%[R_IN])\n\t"			\
713 		  "    la %[R_OUT],1(%[R_OUT])\n\t"			\
714 		  "    brctg %[R_TMP2],20b\n\t"				\
715 		  "    j 0b\n\t" /* Switch to vx-loop.  */		\
716 		  /* Test if ch is 2-byte UTF-8 char.  */		\
717 		  "22: clfi %[R_TMP],0x7ff\n\t"				\
718 		  "    jh 23f\n\t"					\
719 		  /* Handle 2-byte UTF-8 char.  */			\
720 		  "32: slgfi %[R_OUTLEN],2\n\t"				\
721 		  "    jl 90f \n\t"					\
722 		  "    llill %[R_TMP3],0xc080\n\t"			\
723 		  "    la %[R_IN],2(%[R_IN])\n\t"			\
724 		  "    risbgn %[R_TMP3],%[R_TMP],51,55,2\n\t" /* 1. byte.   */ \
725 		  "    risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 2. byte.   */ \
726 		  "    sth %[R_TMP3],0(%[R_OUT])\n\t"			\
727 		  "    la %[R_OUT],2(%[R_OUT])\n\t"			\
728 		  "    brctg %[R_TMP2],20b\n\t"				\
729 		  "    j 0b\n\t" /* Switch to vx-loop.  */		\
730 		  /* Test if ch is 3-byte UTF-8 char.  */		\
731 		  "23: clfi %[R_TMP],0xd7ff\n\t"			\
732 		  "    jh 24f\n\t"					\
733 		  /* Handle 3-byte UTF-8 char.  */			\
734 		  "33: slgfi %[R_OUTLEN],3\n\t"				\
735 		  "    jl 90f \n\t"					\
736 		  "    llilf %[R_TMP3],0xe08080\n\t"			\
737 		  "    la %[R_IN],2(%[R_IN])\n\t"			\
738 		  "    risbgn %[R_TMP3],%[R_TMP],44,47,4\n\t" /* 1. byte.  */ \
739 		  "    risbgn %[R_TMP3],%[R_TMP],50,55,2\n\t" /* 2. byte.  */ \
740 		  "    risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 3. byte.  */ \
741 		  "    stcm %[R_TMP3],7,0(%[R_OUT])\n\t"		\
742 		  "    la %[R_OUT],3(%[R_OUT])\n\t"			\
743 		  "    brctg %[R_TMP2],20b\n\t"				\
744 		  "    j 0b\n\t" /* Switch to vx-loop.  */		\
745 		  /* Test if ch is 4-byte UTF-8 char.  */		\
746 		  "24: clfi %[R_TMP],0xdfff\n\t"			\
747 		  "    jh 33b\n\t" /* Handle this 3-byte UTF-8 char.  */ \
748 		  "    clfi %[R_TMP],0xdbff\n\t"			\
749 		  "    locghih %[R_RES],%[RES_IN_ILL]\n\t"		\
750 		  "    jh 99f\n\t" /* Jump away if this is a low surrogate \
751 				      without a preceding high surrogate.  */ \
752 		  /* Handle 4-byte UTF-8 char.  */			\
753 		  "34: slgfi %[R_OUTLEN],4\n\t"				\
754 		  "    jl 90f \n\t"					\
755 		  "    slgfi %[R_INLEN],2\n\t"				\
756 		  "    locghil %[R_RES],%[RES_IN_FULL]\n\t"		\
757 		  "    jl 99f\n\t" /* Jump away if low surrogate is missing.  */ \
758 		  "    llilf %[R_TMP3],0xf0808080\n\t"			\
759 		  "    aghi %[R_TMP],0x40\n\t"				\
760 		  "    risbgn %[R_TMP3],%[R_TMP],37,39,16\n\t" /* 1. byte: uvw  */ \
761 		  "    risbgn %[R_TMP3],%[R_TMP],42,43,14\n\t" /* 2. byte: xy  */ \
762 		  "    risbgn %[R_TMP3],%[R_TMP],44,47,14\n\t" /* 2. byte: efgh  */ \
763 		  "    risbgn %[R_TMP3],%[R_TMP],50,51,12\n\t" /* 3. byte: ij */ \
764 		  "    llh %[R_TMP],2(%[R_IN])\n\t" /* Load low surrogate.  */ \
765 		  "    risbgn %[R_TMP3],%[R_TMP],52,55,2\n\t" /* 3. byte: klmn  */ \
766 		  "    risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 4. byte: opqrst  */ \
767 		  "    nilf %[R_TMP],0xfc00\n\t"			\
768 		  "    clfi %[R_TMP],0xdc00\n\t" /* Check if it starts with 0xdc00.  */ \
769 		  "    locghine %[R_RES],%[RES_IN_ILL]\n\t"		\
770 		  "    jne 99f\n\t" /* Jump away if low surrogate is invalid.  */ \
771 		  "    st %[R_TMP3],0(%[R_OUT])\n\t"			\
772 		  "    la %[R_IN],4(%[R_IN])\n\t"			\
773 		  "    la %[R_OUT],4(%[R_OUT])\n\t"			\
774 		  "    aghi %[R_TMP2],-2\n\t"				\
775 		  "    jh 20b\n\t"					\
776 		  "    j 0b\n\t" /* Switch to vx-loop.  */		\
777 		  /* Exit with __GCONV_FULL_OUTPUT.  */			\
778 		  "90: lghi %[R_RES],%[RES_OUT_FULL]\n\t"		\
779 		  "99: \n\t"						\
780 		  ".machine pop"					\
781 		  : /* outputs */ [R_IN] "+a" (inptr)			\
782 		    , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (outptr)	\
783 		    , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp)	\
784 		    , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3)	\
785 		    , [R_RES] "+d" (result)				\
786 		  : /* inputs */					\
787 		    [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT)		\
788 		    , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT)		\
789 		    , [RES_IN_FULL] "i" (__GCONV_INCOMPLETE_INPUT)	\
790 		  : /* clobber list */ "memory", "cc"			\
791 		    ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17")	\
792 		    ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19")	\
793 		    ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31")	\
794 		  );							\
795     if (__glibc_likely (inptr == inend)					\
796 	|| result != __GCONV_ILLEGAL_INPUT)				\
797       break;								\
798 									\
799     STANDARD_TO_LOOP_ERR_HANDLER (2);					\
800   }
801 
802 /* Generate loop-function with vector implementation.  */
803 # define MIN_NEEDED_INPUT	MIN_NEEDED_TO
804 # define MAX_NEEDED_INPUT	MAX_NEEDED_TO
805 # define MIN_NEEDED_OUTPUT	MIN_NEEDED_FROM
806 # define MAX_NEEDED_OUTPUT	MAX_NEEDED_FROM
807 # define TO_LOOP_VX		__to_utf8_loop_vx
808 # define LOOPFCT		TO_LOOP_VX
809 # define BODY                   BODY_TO_VX
810 # define LOOP_NEED_FLAGS
811 # include <iconv/loop.c>
812 #else
813 # define TO_LOOP_VX		NULL
814 #endif /* HAVE_TO_VX != 1  */
815 
816 #if HAVE_TO_VX_CU == 1
817 #define BODY_TO_VX_CU							\
818   {									\
819     register const unsigned char* pInput asm ("8") = inptr;		\
820     register size_t inlen asm ("9") = inend - inptr;			\
821     register unsigned char* pOutput asm ("10") = outptr;		\
822     register size_t outlen asm ("11") = outend - outptr;		\
823     unsigned long tmp, tmp2, tmp3;					\
824     asm volatile (".machine push\n\t"					\
825 		  ".machine \"z13\"\n\t"				\
826 		  ".machinemode \"zarch_nohighgprs\"\n\t"		\
827 		  /* Setup to check for values <= 0x7f.  */		\
828 		  "    larl %[R_TMP],9f\n\t"				\
829 		  "    vlm %%v30,%%v31,0(%[R_TMP])\n\t"			\
830 		  CONVERT_32BIT_SIZE_T ([R_INLEN])			\
831 		  CONVERT_32BIT_SIZE_T ([R_OUTLEN])			\
832 		  /* Loop which handles UTF-16 chars <=0x7f.  */	\
833 		  "0:  clgijl %[R_INLEN],32,20f\n\t"			\
834 		  "    clgijl %[R_OUTLEN],16,20f\n\t"			\
835 		  "1:  vlm %%v16,%%v17,0(%[R_IN])\n\t"			\
836 		  "    lghi %[R_TMP2],0\n\t"				\
837 		  /* Check for > 1byte UTF-8 chars.  */			\
838 		  "    vstrchs %%v19,%%v16,%%v30,%%v31\n\t"		\
839 		  "    jno 10f\n\t" /* Jump away if not all bytes are 1byte \
840 				       UTF8 chars.  */			\
841 		  "    vstrchs %%v19,%%v17,%%v30,%%v31\n\t"		\
842 		  "    jno 11f\n\t" /* Jump away if not all bytes are 1byte \
843 				       UTF8 chars.  */			\
844 		  /* Shorten to UTF-8.  */				\
845 		  "    vpkh %%v18,%%v16,%%v17\n\t"			\
846 		  "    la %[R_IN],32(%[R_IN])\n\t"			\
847 		  "    aghi %[R_INLEN],-32\n\t"				\
848 		  /* Store 16 bytes to buf_out.  */			\
849 		  "    vst %%v18,0(%[R_OUT])\n\t"			\
850 		  "    aghi %[R_OUTLEN],-16\n\t"			\
851 		  "    la %[R_OUT],16(%[R_OUT])\n\t"			\
852 		  "    clgijl %[R_INLEN],32,20f\n\t"			\
853 		  "    clgijl %[R_OUTLEN],16,20f\n\t"			\
854 		  "    j 1b\n\t"					\
855 		  /* Setup to check for ch > 0x7f. (v30, v31)  */	\
856 		  "9:  .short 0x7f,0x7f,0x0,0x0,0x0,0x0,0x0,0x0\n\t"	\
857 		  "    .short 0x2000,0x2000,0x0,0x0,0x0,0x0,0x0,0x0\n\t" \
858 		  /* At least one byte is > 0x7f.			\
859 		     Store the preceding 1-byte chars.  */		\
860 		  "11: lghi %[R_TMP2],16\n\t" /* match was found in v17.  */ \
861 		  "10: vlgvb %[R_TMP],%%v19,7\n\t"			\
862 		  /* Shorten to UTF-8.  */				\
863 		  "    vpkh %%v18,%%v16,%%v17\n\t"			\
864 		  "    ar %[R_TMP],%[R_TMP2]\n\t" /* Number of in bytes.  */ \
865 		  "    srlg %[R_TMP3],%[R_TMP],1\n\t" /* Number of out bytes.  */ \
866 		  "    ahik %[R_TMP2],%[R_TMP3],-1\n\t" /* Highest index to store.  */ \
867 		  "    jl 20f\n\t"					\
868 		  "    vstl %%v18,%[R_TMP2],0(%[R_OUT])\n\t"		\
869 		  /* Update pointers.  */				\
870 		  "    la %[R_IN],0(%[R_TMP],%[R_IN])\n\t"		\
871 		  "    slgr %[R_INLEN],%[R_TMP]\n\t"			\
872 		  "    la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t"		\
873 		  "    slgr %[R_OUTLEN],%[R_TMP3]\n\t"			\
874 		  /* Handles UTF16 surrogates with convert instruction.  */ \
875 		  "20: cu21 %[R_OUT],%[R_IN],1\n\t"			\
876 		  "    jo 0b\n\t" /* Try vector implemenation again.  */ \
877 		  "    lochil %[R_RES],%[RES_OUT_FULL]\n\t" /* cc == 1.  */ \
878 		  "    lochih %[R_RES],%[RES_IN_ILL]\n\t" /* cc == 2.  */ \
879 		  ".machine pop"					\
880 		  : /* outputs */ [R_IN] "+a" (pInput)			\
881 		    , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (pOutput)	\
882 		    , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp)	\
883 		    , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3)	\
884 		    , [R_RES] "+d" (result)				\
885 		  : /* inputs */					\
886 		    [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT)		\
887 		    , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT)		\
888 		  : /* clobber list */ "memory", "cc"			\
889 		    ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17")	\
890 		    ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19")	\
891 		    ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31")	\
892 		  );							\
893     inptr = pInput;							\
894     outptr = pOutput;							\
895 									\
896     if (__glibc_likely (inlen == 0)					\
897 	|| result == __GCONV_FULL_OUTPUT)				\
898       break;								\
899     if (inlen == 1)							\
900       {									\
901 	/* Input does not contain a complete utf16 character.  */	\
902 	result = __GCONV_INCOMPLETE_INPUT;				\
903 	break;								\
904       }									\
905     else if (result != __GCONV_ILLEGAL_INPUT)				\
906       {									\
907 	/* Input is >= 2 and < 4 bytes (as cu21 would have processed	\
908 	   a possible next utf16 character) and not illegal.		\
909 	   => we have a single high surrogate at end of input.  */	\
910 	result = __GCONV_INCOMPLETE_INPUT;				\
911 	break;								\
912       }									\
913 									\
914     STANDARD_TO_LOOP_ERR_HANDLER (2);					\
915   }
916 
917 /* Generate loop-function with vector and utf-convert instructions.  */
918 # define MIN_NEEDED_INPUT	MIN_NEEDED_TO
919 # define MAX_NEEDED_INPUT	MAX_NEEDED_TO
920 # define MIN_NEEDED_OUTPUT	MIN_NEEDED_FROM
921 # define MAX_NEEDED_OUTPUT	MAX_NEEDED_FROM
922 # define TO_LOOP_VX_CU		__to_utf8_loop_vx_cu
923 # define LOOPFCT		TO_LOOP_VX_CU
924 # define BODY                   BODY_TO_VX_CU
925 # define LOOP_NEED_FLAGS
926 # include <iconv/loop.c>
927 #else
928 # define TO_LOOP_VX_CU		NULL
929 #endif /* HAVE_TO_VX_CU != 1  */
930 
931 /* This file also exists in sysdeps/s390/multiarch/ which
932    generates ifunc resolvers for FROM/TO_LOOP functions
933    and includes iconv/skeleton.c afterwards.  */
934 #if ! defined USE_MULTIARCH
935 # include <iconv/skeleton.c>
936 #endif
937