Replace M_El with lit_e in libm-test.inc
[glibc.git] / sysdeps / s390 / utf8-utf32-z9.c
blobefae7456c4679f833bdfc84333e2fc76f23401f2
1 /* Conversion between UTF-8 and UTF-32 BE/internal.
3 This module uses the Z9-109 variants of the Convert Unicode
4 instructions.
5 Copyright (C) 1997-2016 Free Software Foundation, Inc.
7 Author: Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
8 Based on the work by Ulrich Drepper <drepper@cygnus.com>, 1997.
10 Thanks to Daniel Appich who covered the relevant performance work
11 in his diploma thesis.
13 This is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2.1 of the License, or (at your option) any later version.
18 This is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
23 You should have received a copy of the GNU Lesser General Public
24 License along with the GNU C Library; if not, see
25 <http://www.gnu.org/licenses/>. */
27 #include <dlfcn.h>
28 #include <stdint.h>
29 #include <unistd.h>
30 #include <dl-procinfo.h>
31 #include <gconv.h>
33 #if defined HAVE_S390_VX_GCC_SUPPORT
34 # define ASM_CLOBBER_VR(NR) , NR
35 #else
36 # define ASM_CLOBBER_VR(NR)
37 #endif
39 #if defined __s390x__
40 # define CONVERT_32BIT_SIZE_T(REG)
41 #else
42 # define CONVERT_32BIT_SIZE_T(REG) "llgfr %" #REG ",%" #REG "\n\t"
43 #endif
45 /* Defines for skeleton.c. */
46 #define DEFINE_INIT 0
47 #define DEFINE_FINI 0
48 #define MIN_NEEDED_FROM 1
49 #define MAX_NEEDED_FROM 6
50 #define MIN_NEEDED_TO 4
51 #define FROM_LOOP __from_utf8_loop
52 #define TO_LOOP __to_utf8_loop
53 #define FROM_DIRECTION (dir == from_utf8)
54 #define ONE_DIRECTION 0
56 /* UTF-32 big endian byte order mark. */
57 #define BOM 0x0000feffu
59 /* Direction of the transformation. */
60 enum direction
62 illegal_dir,
63 to_utf8,
64 from_utf8
67 struct utf8_data
69 enum direction dir;
70 int emit_bom;
74 extern int gconv_init (struct __gconv_step *step);
75 int
76 gconv_init (struct __gconv_step *step)
78 /* Determine which direction. */
79 struct utf8_data *new_data;
80 enum direction dir = illegal_dir;
81 int emit_bom;
82 int result;
84 emit_bom = (__strcasecmp (step->__to_name, "UTF-32//") == 0);
86 if (__strcasecmp (step->__from_name, "ISO-10646/UTF8/") == 0
87 && (__strcasecmp (step->__to_name, "UTF-32//") == 0
88 || __strcasecmp (step->__to_name, "UTF-32BE//") == 0
89 || __strcasecmp (step->__to_name, "INTERNAL") == 0))
91 dir = from_utf8;
93 else if (__strcasecmp (step->__to_name, "ISO-10646/UTF8/") == 0
94 && (__strcasecmp (step->__from_name, "UTF-32BE//") == 0
95 || __strcasecmp (step->__from_name, "INTERNAL") == 0))
97 dir = to_utf8;
100 result = __GCONV_NOCONV;
101 if (dir != illegal_dir)
103 new_data = (struct utf8_data *) malloc (sizeof (struct utf8_data));
105 result = __GCONV_NOMEM;
106 if (new_data != NULL)
108 new_data->dir = dir;
109 new_data->emit_bom = emit_bom;
110 step->__data = new_data;
112 if (dir == from_utf8)
114 step->__min_needed_from = MIN_NEEDED_FROM;
115 step->__max_needed_from = MIN_NEEDED_FROM;
116 step->__min_needed_to = MIN_NEEDED_TO;
117 step->__max_needed_to = MIN_NEEDED_TO;
119 else
121 step->__min_needed_from = MIN_NEEDED_TO;
122 step->__max_needed_from = MIN_NEEDED_TO;
123 step->__min_needed_to = MIN_NEEDED_FROM;
124 step->__max_needed_to = MIN_NEEDED_FROM;
127 step->__stateful = 0;
129 result = __GCONV_OK;
133 return result;
137 extern void gconv_end (struct __gconv_step *data);
138 void
139 gconv_end (struct __gconv_step *data)
141 free (data->__data);
144 /* The macro for the hardware loop. This is used for both
145 directions. */
146 #define HARDWARE_CONVERT(INSTRUCTION) \
148 register const unsigned char* pInput __asm__ ("8") = inptr; \
149 register size_t inlen __asm__ ("9") = inend - inptr; \
150 register unsigned char* pOutput __asm__ ("10") = outptr; \
151 register size_t outlen __asm__("11") = outend - outptr; \
152 unsigned long cc = 0; \
154 __asm__ __volatile__ (".machine push \n\t" \
155 ".machine \"z9-109\" \n\t" \
156 ".machinemode \"zarch_nohighgprs\"\n\t" \
157 "0: " INSTRUCTION " \n\t" \
158 ".machine pop \n\t" \
159 " jo 0b \n\t" \
160 " ipm %2 \n" \
161 : "+a" (pOutput), "+a" (pInput), "+d" (cc), \
162 "+d" (outlen), "+d" (inlen) \
164 : "cc", "memory"); \
166 inptr = pInput; \
167 outptr = pOutput; \
168 cc >>= 28; \
170 if (cc == 1) \
172 result = __GCONV_FULL_OUTPUT; \
174 else if (cc == 2) \
176 result = __GCONV_ILLEGAL_INPUT; \
180 #define PREPARE_LOOP \
181 enum direction dir = ((struct utf8_data *) step->__data)->dir; \
182 int emit_bom = ((struct utf8_data *) step->__data)->emit_bom; \
184 if (emit_bom && !data->__internal_use \
185 && data->__invocation_counter == 0) \
187 /* Emit the Byte Order Mark. */ \
188 if (__glibc_unlikely (outbuf + 4 > outend)) \
189 return __GCONV_FULL_OUTPUT; \
191 put32u (outbuf, BOM); \
192 outbuf += 4; \
195 /* Conversion function from UTF-8 to UTF-32 internal/BE. */
197 #define STORE_REST_COMMON \
199 /* We store the remaining bytes while converting them into the UCS4 \
200 format. We can assume that the first byte in the buffer is \
201 correct and that it requires a larger number of bytes than there \
202 are in the input buffer. */ \
203 wint_t ch = **inptrp; \
204 size_t cnt, r; \
206 state->__count = inend - *inptrp; \
208 assert (ch != 0xc0 && ch != 0xc1); \
209 if (ch >= 0xc2 && ch < 0xe0) \
211 /* We expect two bytes. The first byte cannot be 0xc0 or \
212 0xc1, otherwise the wide character could have been \
213 represented using a single byte. */ \
214 cnt = 2; \
215 ch &= 0x1f; \
217 else if (__glibc_likely ((ch & 0xf0) == 0xe0)) \
219 /* We expect three bytes. */ \
220 cnt = 3; \
221 ch &= 0x0f; \
223 else if (__glibc_likely ((ch & 0xf8) == 0xf0)) \
225 /* We expect four bytes. */ \
226 cnt = 4; \
227 ch &= 0x07; \
229 else if (__glibc_likely ((ch & 0xfc) == 0xf8)) \
231 /* We expect five bytes. */ \
232 cnt = 5; \
233 ch &= 0x03; \
235 else \
237 /* We expect six bytes. */ \
238 cnt = 6; \
239 ch &= 0x01; \
242 /* The first byte is already consumed. */ \
243 r = cnt - 1; \
244 while (++(*inptrp) < inend) \
246 ch <<= 6; \
247 ch |= **inptrp & 0x3f; \
248 --r; \
251 /* Shift for the so far missing bytes. */ \
252 ch <<= r * 6; \
254 /* Store the number of bytes expected for the entire sequence. */ \
255 state->__count |= cnt << 8; \
257 /* Store the value. */ \
258 state->__value.__wch = ch; \
261 #define UNPACK_BYTES_COMMON \
263 static const unsigned char inmask[5] = { 0xc0, 0xe0, 0xf0, 0xf8, 0xfc }; \
264 wint_t wch = state->__value.__wch; \
265 size_t ntotal = state->__count >> 8; \
267 inlen = state->__count & 255; \
269 bytebuf[0] = inmask[ntotal - 2]; \
271 do \
273 if (--ntotal < inlen) \
274 bytebuf[ntotal] = 0x80 | (wch & 0x3f); \
275 wch >>= 6; \
277 while (ntotal > 1); \
279 bytebuf[0] |= wch; \
282 #define CLEAR_STATE_COMMON \
283 state->__count = 0
285 #define BODY_FROM_HW(ASM) \
287 ASM; \
288 if (__glibc_likely (inptr == inend) \
289 || result == __GCONV_FULL_OUTPUT) \
290 break; \
292 int i; \
293 for (i = 1; inptr + i < inend && i < 5; ++i) \
294 if ((inptr[i] & 0xc0) != 0x80) \
295 break; \
297 if (__glibc_likely (inptr + i == inend \
298 && result == __GCONV_EMPTY_INPUT)) \
300 result = __GCONV_INCOMPLETE_INPUT; \
301 break; \
303 STANDARD_FROM_LOOP_ERR_HANDLER (i); \
306 /* This hardware routine uses the Convert UTF8 to UTF32 (cu14) instruction. */
307 #define BODY_FROM_ETF3EH BODY_FROM_HW (HARDWARE_CONVERT ("cu14 %0, %1, 1"))
310 /* The software routine is copied from gconv_simple.c. */
311 #define BODY_FROM_C \
313 /* Next input byte. */ \
314 uint32_t ch = *inptr; \
316 if (__glibc_likely (ch < 0x80)) \
318 /* One byte sequence. */ \
319 ++inptr; \
321 else \
323 uint_fast32_t cnt; \
324 uint_fast32_t i; \
326 if (ch >= 0xc2 && ch < 0xe0) \
328 /* We expect two bytes. The first byte cannot be 0xc0 or \
329 0xc1, otherwise the wide character could have been \
330 represented using a single byte. */ \
331 cnt = 2; \
332 ch &= 0x1f; \
334 else if (__glibc_likely ((ch & 0xf0) == 0xe0)) \
336 /* We expect three bytes. */ \
337 cnt = 3; \
338 ch &= 0x0f; \
340 else if (__glibc_likely ((ch & 0xf8) == 0xf0)) \
342 /* We expect four bytes. */ \
343 cnt = 4; \
344 ch &= 0x07; \
346 else \
348 /* Search the end of this ill-formed UTF-8 character. This \
349 is the next byte with (x & 0xc0) != 0x80. */ \
350 i = 0; \
351 do \
352 ++i; \
353 while (inptr + i < inend \
354 && (*(inptr + i) & 0xc0) == 0x80 \
355 && i < 5); \
357 errout: \
358 STANDARD_FROM_LOOP_ERR_HANDLER (i); \
361 if (__glibc_unlikely (inptr + cnt > inend)) \
363 /* We don't have enough input. But before we report \
364 that check that all the bytes are correct. */ \
365 for (i = 1; inptr + i < inend; ++i) \
366 if ((inptr[i] & 0xc0) != 0x80) \
367 break; \
369 if (__glibc_likely (inptr + i == inend)) \
371 result = __GCONV_INCOMPLETE_INPUT; \
372 break; \
375 goto errout; \
378 /* Read the possible remaining bytes. */ \
379 for (i = 1; i < cnt; ++i) \
381 uint32_t byte = inptr[i]; \
383 if ((byte & 0xc0) != 0x80) \
384 /* This is an illegal encoding. */ \
385 break; \
387 ch <<= 6; \
388 ch |= byte & 0x3f; \
391 /* If i < cnt, some trail byte was not >= 0x80, < 0xc0. \
392 If cnt > 2 and ch < 2^(5*cnt-4), the wide character ch could \
393 have been represented with fewer than cnt bytes. */ \
394 if (i < cnt || (cnt > 2 && (ch >> (5 * cnt - 4)) == 0) \
395 /* Do not accept UTF-16 surrogates. */ \
396 || (ch >= 0xd800 && ch <= 0xdfff) \
397 || (ch > 0x10ffff)) \
399 /* This is an illegal encoding. */ \
400 goto errout; \
403 inptr += cnt; \
406 /* Now adjust the pointers and store the result. */ \
407 *((uint32_t *) outptr) = ch; \
408 outptr += sizeof (uint32_t); \
411 #define HW_FROM_VX \
413 register const unsigned char* pInput asm ("8") = inptr; \
414 register size_t inlen asm ("9") = inend - inptr; \
415 register unsigned char* pOutput asm ("10") = outptr; \
416 register size_t outlen asm("11") = outend - outptr; \
417 unsigned long tmp, tmp2, tmp3; \
418 asm volatile (".machine push\n\t" \
419 ".machine \"z13\"\n\t" \
420 ".machinemode \"zarch_nohighgprs\"\n\t" \
421 " vrepib %%v30,0x7f\n\t" /* For compare > 0x7f. */ \
422 " vrepib %%v31,0x20\n\t" \
423 CONVERT_32BIT_SIZE_T ([R_INLEN]) \
424 CONVERT_32BIT_SIZE_T ([R_OUTLEN]) \
425 /* Loop which handles UTF-8 chars <=0x7f. */ \
426 "0: clgijl %[R_INLEN],16,20f\n\t" \
427 " clgijl %[R_OUTLEN],64,20f\n\t" \
428 "1: vl %%v16,0(%[R_IN])\n\t" \
429 " vstrcbs %%v17,%%v16,%%v30,%%v31\n\t" \
430 " jno 10f\n\t" /* Jump away if not all bytes are 1byte \
431 UTF8 chars. */ \
432 /* Enlarge to UCS4. */ \
433 " vuplhb %%v18,%%v16\n\t" \
434 " vupllb %%v19,%%v16\n\t" \
435 " la %[R_IN],16(%[R_IN])\n\t" \
436 " vuplhh %%v20,%%v18\n\t" \
437 " aghi %[R_INLEN],-16\n\t" \
438 " vupllh %%v21,%%v18\n\t" \
439 " aghi %[R_OUTLEN],-64\n\t" \
440 " vuplhh %%v22,%%v19\n\t" \
441 " vupllh %%v23,%%v19\n\t" \
442 /* Store 64 bytes to buf_out. */ \
443 " vstm %%v20,%%v23,0(%[R_OUT])\n\t" \
444 " la %[R_OUT],64(%[R_OUT])\n\t" \
445 " clgijl %[R_INLEN],16,20f\n\t" \
446 " clgijl %[R_OUTLEN],64,20f\n\t" \
447 " j 1b\n\t" \
448 "10: \n\t" \
449 /* At least one byte is > 0x7f. \
450 Store the preceding 1-byte chars. */ \
451 " vlgvb %[R_TMP],%%v17,7\n\t" \
452 " sllk %[R_TMP2],%[R_TMP],2\n\t" /* Compute highest \
453 index to store. */ \
454 " llgfr %[R_TMP3],%[R_TMP2]\n\t" \
455 " ahi %[R_TMP2],-1\n\t" \
456 " jl 20f\n\t" \
457 " vuplhb %%v18,%%v16\n\t" \
458 " vuplhh %%v20,%%v18\n\t" \
459 " vstl %%v20,%[R_TMP2],0(%[R_OUT])\n\t" \
460 " ahi %[R_TMP2],-16\n\t" \
461 " jl 11f\n\t" \
462 " vupllh %%v21,%%v18\n\t" \
463 " vstl %%v21,%[R_TMP2],16(%[R_OUT])\n\t" \
464 " ahi %[R_TMP2],-16\n\t" \
465 " jl 11f\n\t" \
466 " vupllb %%v19,%%v16\n\t" \
467 " vuplhh %%v22,%%v19\n\t" \
468 " vstl %%v22,%[R_TMP2],32(%[R_OUT])\n\t" \
469 " ahi %[R_TMP2],-16\n\t" \
470 " jl 11f\n\t" \
471 " vupllh %%v23,%%v19\n\t" \
472 " vstl %%v23,%[R_TMP2],48(%[R_OUT])\n\t" \
473 "11: \n\t" \
474 /* Update pointers. */ \
475 " la %[R_IN],0(%[R_TMP],%[R_IN])\n\t" \
476 " slgr %[R_INLEN],%[R_TMP]\n\t" \
477 " la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t" \
478 " slgr %[R_OUTLEN],%[R_TMP3]\n\t" \
479 /* Handle multibyte utf8-char with convert instruction. */ \
480 "20: cu14 %[R_OUT],%[R_IN],1\n\t" \
481 " jo 0b\n\t" /* Try vector implemenation again. */ \
482 " lochil %[R_RES],%[RES_OUT_FULL]\n\t" /* cc == 1. */ \
483 " lochih %[R_RES],%[RES_IN_ILL]\n\t" /* cc == 2. */ \
484 ".machine pop" \
485 : /* outputs */ [R_IN] "+a" (pInput) \
486 , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (pOutput) \
487 , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp) \
488 , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3) \
489 , [R_RES] "+d" (result) \
490 : /* inputs */ \
491 [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT) \
492 , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT) \
493 : /* clobber list */ "memory", "cc" \
494 ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17") \
495 ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19") \
496 ASM_CLOBBER_VR ("v20") ASM_CLOBBER_VR ("v21") \
497 ASM_CLOBBER_VR ("v22") ASM_CLOBBER_VR ("v30") \
498 ASM_CLOBBER_VR ("v31") \
499 ); \
500 inptr = pInput; \
501 outptr = pOutput; \
503 #define BODY_FROM_VX BODY_FROM_HW (HW_FROM_VX)
505 /* These definitions apply to the UTF-8 to UTF-32 direction. The
506 software implementation for UTF-8 still supports multibyte
507 characters up to 6 bytes whereas the hardware variant does not. */
508 #define MIN_NEEDED_INPUT MIN_NEEDED_FROM
509 #define MAX_NEEDED_INPUT MAX_NEEDED_FROM
510 #define MIN_NEEDED_OUTPUT MIN_NEEDED_TO
511 #define LOOPFCT __from_utf8_loop_c
513 #define LOOP_NEED_FLAGS
515 #define STORE_REST STORE_REST_COMMON
516 #define UNPACK_BYTES UNPACK_BYTES_COMMON
517 #define CLEAR_STATE CLEAR_STATE_COMMON
518 #define BODY BODY_FROM_C
519 #include <iconv/loop.c>
522 /* Generate loop-function with hardware utf-convert instruction. */
523 #define MIN_NEEDED_INPUT MIN_NEEDED_FROM
524 #define MAX_NEEDED_INPUT MAX_NEEDED_FROM
525 #define MIN_NEEDED_OUTPUT MIN_NEEDED_TO
526 #define LOOPFCT __from_utf8_loop_etf3eh
528 #define LOOP_NEED_FLAGS
530 #define STORE_REST STORE_REST_COMMON
531 #define UNPACK_BYTES UNPACK_BYTES_COMMON
532 #define CLEAR_STATE CLEAR_STATE_COMMON
533 #define BODY BODY_FROM_ETF3EH
534 #include <iconv/loop.c>
536 #if defined HAVE_S390_VX_ASM_SUPPORT
537 /* Generate loop-function with hardware vector instructions. */
538 # define MIN_NEEDED_INPUT MIN_NEEDED_FROM
539 # define MAX_NEEDED_INPUT MAX_NEEDED_FROM
540 # define MIN_NEEDED_OUTPUT MIN_NEEDED_TO
541 # define LOOPFCT __from_utf8_loop_vx
543 # define LOOP_NEED_FLAGS
545 # define STORE_REST STORE_REST_COMMON
546 # define UNPACK_BYTES UNPACK_BYTES_COMMON
547 # define CLEAR_STATE CLEAR_STATE_COMMON
548 # define BODY BODY_FROM_VX
549 # include <iconv/loop.c>
550 #endif
553 /* Generate ifunc'ed loop function. */
554 __typeof(__from_utf8_loop_c)
555 __attribute__ ((ifunc ("__from_utf8_loop_resolver")))
556 __from_utf8_loop;
558 static void *
559 __from_utf8_loop_resolver (unsigned long int dl_hwcap)
561 #if defined HAVE_S390_VX_ASM_SUPPORT
562 if (dl_hwcap & HWCAP_S390_VX)
563 return __from_utf8_loop_vx;
564 else
565 #endif
566 if (dl_hwcap & HWCAP_S390_ZARCH && dl_hwcap & HWCAP_S390_HIGH_GPRS
567 && dl_hwcap & HWCAP_S390_ETF3EH)
568 return __from_utf8_loop_etf3eh;
569 else
570 return __from_utf8_loop_c;
573 strong_alias (__from_utf8_loop_c_single, __from_utf8_loop_single)
575 /* The software routine mimics the S/390 cu41 instruction. */
576 #define BODY_TO_C \
578 uint32_t wc = *((const uint32_t *) inptr); \
580 if (__glibc_likely (wc <= 0x7f)) \
582 /* Single UTF-8 char. */ \
583 *outptr = (uint8_t)wc; \
584 outptr++; \
586 else if (wc <= 0x7ff) \
588 /* Two UTF-8 chars. */ \
589 if (__glibc_unlikely (outptr + 2 > outend)) \
591 /* Overflow in the output buffer. */ \
592 result = __GCONV_FULL_OUTPUT; \
593 break; \
596 outptr[0] = 0xc0; \
597 outptr[0] |= wc >> 6; \
599 outptr[1] = 0x80; \
600 outptr[1] |= wc & 0x3f; \
602 outptr += 2; \
604 else if (wc <= 0xffff) \
606 /* Three UTF-8 chars. */ \
607 if (__glibc_unlikely (outptr + 3 > outend)) \
609 /* Overflow in the output buffer. */ \
610 result = __GCONV_FULL_OUTPUT; \
611 break; \
613 if (wc >= 0xd800 && wc <= 0xdfff) \
615 /* Do not accept UTF-16 surrogates. */ \
616 result = __GCONV_ILLEGAL_INPUT; \
617 STANDARD_TO_LOOP_ERR_HANDLER (4); \
619 outptr[0] = 0xe0; \
620 outptr[0] |= wc >> 12; \
622 outptr[1] = 0x80; \
623 outptr[1] |= (wc >> 6) & 0x3f; \
625 outptr[2] = 0x80; \
626 outptr[2] |= wc & 0x3f; \
628 outptr += 3; \
630 else if (wc <= 0x10ffff) \
632 /* Four UTF-8 chars. */ \
633 if (__glibc_unlikely (outptr + 4 > outend)) \
635 /* Overflow in the output buffer. */ \
636 result = __GCONV_FULL_OUTPUT; \
637 break; \
639 outptr[0] = 0xf0; \
640 outptr[0] |= wc >> 18; \
642 outptr[1] = 0x80; \
643 outptr[1] |= (wc >> 12) & 0x3f; \
645 outptr[2] = 0x80; \
646 outptr[2] |= (wc >> 6) & 0x3f; \
648 outptr[3] = 0x80; \
649 outptr[3] |= wc & 0x3f; \
651 outptr += 4; \
653 else \
655 STANDARD_TO_LOOP_ERR_HANDLER (4); \
657 inptr += 4; \
660 /* The hardware routine uses the S/390 vector instructions. */
661 #define BODY_TO_VX \
663 size_t inlen = inend - inptr; \
664 size_t outlen = outend - outptr; \
665 unsigned long tmp, tmp2, tmp3; \
666 asm volatile (".machine push\n\t" \
667 ".machine \"z13\"\n\t" \
668 ".machinemode \"zarch_nohighgprs\"\n\t" \
669 " vleif %%v20,127,0\n\t" /* element 0: 127 */ \
670 " vzero %%v21\n\t" \
671 " vleih %%v21,8192,0\n\t" /* element 0: > */ \
672 " vleih %%v21,-8192,2\n\t" /* element 1: =<> */ \
673 CONVERT_32BIT_SIZE_T ([R_INLEN]) \
674 CONVERT_32BIT_SIZE_T ([R_OUTLEN]) \
675 /* Loop which handles UTF-32 chars <=0x7f. */ \
676 "0: clgijl %[R_INLEN],64,2f\n\t" \
677 " clgijl %[R_OUTLEN],16,2f\n\t" \
678 "1: vlm %%v16,%%v19,0(%[R_IN])\n\t" \
679 " lghi %[R_TMP2],0\n\t" \
680 /* Shorten to byte values. */ \
681 " vpkf %%v23,%%v16,%%v17\n\t" \
682 " vpkf %%v24,%%v18,%%v19\n\t" \
683 " vpkh %%v23,%%v23,%%v24\n\t" \
684 /* Checking for values > 0x7f. */ \
685 " vstrcfs %%v22,%%v16,%%v20,%%v21\n\t" \
686 " jno 10f\n\t" \
687 " vstrcfs %%v22,%%v17,%%v20,%%v21\n\t" \
688 " jno 11f\n\t" \
689 " vstrcfs %%v22,%%v18,%%v20,%%v21\n\t" \
690 " jno 12f\n\t" \
691 " vstrcfs %%v22,%%v19,%%v20,%%v21\n\t" \
692 " jno 13f\n\t" \
693 /* Store 16bytes to outptr. */ \
694 " vst %%v23,0(%[R_OUT])\n\t" \
695 " aghi %[R_INLEN],-64\n\t" \
696 " aghi %[R_OUTLEN],-16\n\t" \
697 " la %[R_IN],64(%[R_IN])\n\t" \
698 " la %[R_OUT],16(%[R_OUT])\n\t" \
699 " clgijl %[R_INLEN],64,2f\n\t" \
700 " clgijl %[R_OUTLEN],16,2f\n\t" \
701 " j 1b\n\t" \
702 /* Found a value > 0x7f. */ \
703 "13: ahi %[R_TMP2],4\n\t" \
704 "12: ahi %[R_TMP2],4\n\t" \
705 "11: ahi %[R_TMP2],4\n\t" \
706 "10: vlgvb %[R_TMP],%%v22,7\n\t" \
707 " srlg %[R_TMP],%[R_TMP],2\n\t" \
708 " agr %[R_TMP],%[R_TMP2]\n\t" \
709 " je 16f\n\t" \
710 /* Store characters before invalid one... */ \
711 " slgr %[R_OUTLEN],%[R_TMP]\n\t" \
712 "15: aghi %[R_TMP],-1\n\t" \
713 " vstl %%v23,%[R_TMP],0(%[R_OUT])\n\t" \
714 /* ... and update pointers. */ \
715 " aghi %[R_TMP],1\n\t" \
716 " la %[R_OUT],0(%[R_TMP],%[R_OUT])\n\t" \
717 " sllg %[R_TMP2],%[R_TMP],2\n\t" \
718 " la %[R_IN],0(%[R_TMP2],%[R_IN])\n\t" \
719 " slgr %[R_INLEN],%[R_TMP2]\n\t" \
720 /* Calculate remaining uint32_t values in loaded vrs. */ \
721 "16: lghi %[R_TMP2],16\n\t" \
722 " sgr %[R_TMP2],%[R_TMP]\n\t" \
723 " l %[R_TMP],0(%[R_IN])\n\t" \
724 " aghi %[R_INLEN],-4\n\t" \
725 " j 22f\n\t" \
726 /* Handle remaining bytes. */ \
727 "2: clgije %[R_INLEN],0,99f\n\t" \
728 " clgijl %[R_INLEN],4,92f\n\t" \
729 /* Calculate remaining uint32_t values in inptr. */ \
730 " srlg %[R_TMP2],%[R_INLEN],2\n\t" \
731 /* Handle multibyte utf8-char. */ \
732 "20: l %[R_TMP],0(%[R_IN])\n\t" \
733 " aghi %[R_INLEN],-4\n\t" \
734 /* Test if ch is 1byte UTF-8 char. */ \
735 "21: clijh %[R_TMP],0x7f,22f\n\t" \
736 /* Handle 1-byte UTF-8 char. */ \
737 "31: slgfi %[R_OUTLEN],1\n\t" \
738 " jl 90f \n\t" \
739 " stc %[R_TMP],0(%[R_OUT])\n\t" \
740 " la %[R_IN],4(%[R_IN])\n\t" \
741 " la %[R_OUT],1(%[R_OUT])\n\t" \
742 " brctg %[R_TMP2],20b\n\t" \
743 " j 0b\n\t" /* Switch to vx-loop. */ \
744 /* Test if ch is 2byte UTF-8 char. */ \
745 "22: clfi %[R_TMP],0x7ff\n\t" \
746 " jh 23f\n\t" \
747 /* Handle 2-byte UTF-8 char. */ \
748 "32: slgfi %[R_OUTLEN],2\n\t" \
749 " jl 90f \n\t" \
750 " llill %[R_TMP3],0xc080\n\t" \
751 " risbgn %[R_TMP3],%[R_TMP],51,55,2\n\t" /* 1. byte. */ \
752 " risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 2. byte. */ \
753 " sth %[R_TMP3],0(%[R_OUT])\n\t" \
754 " la %[R_IN],4(%[R_IN])\n\t" \
755 " la %[R_OUT],2(%[R_OUT])\n\t" \
756 " brctg %[R_TMP2],20b\n\t" \
757 " j 0b\n\t" /* Switch to vx-loop. */ \
758 /* Test if ch is 3-byte UTF-8 char. */ \
759 "23: clfi %[R_TMP],0xffff\n\t" \
760 " jh 24f\n\t" \
761 /* Handle 3-byte UTF-8 char. */ \
762 "33: slgfi %[R_OUTLEN],3\n\t" \
763 " jl 90f \n\t" \
764 " llilf %[R_TMP3],0xe08080\n\t" \
765 " risbgn %[R_TMP3],%[R_TMP],44,47,4\n\t" /* 1. byte. */ \
766 " risbgn %[R_TMP3],%[R_TMP],50,55,2\n\t" /* 2. byte. */ \
767 " risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 3. byte. */ \
768 /* Test if ch is a UTF-16 surrogate: ch & 0xf800 == 0xd800 */ \
769 " nilf %[R_TMP],0xf800\n\t" \
770 " clfi %[R_TMP],0xd800\n\t" \
771 " je 91f\n\t" /* Do not accept UTF-16 surrogates. */ \
772 " stcm %[R_TMP3],7,0(%[R_OUT])\n\t" \
773 " la %[R_IN],4(%[R_IN])\n\t" \
774 " la %[R_OUT],3(%[R_OUT])\n\t" \
775 " brctg %[R_TMP2],20b\n\t" \
776 " j 0b\n\t" /* Switch to vx-loop. */ \
777 /* Test if ch is 4-byte UTF-8 char. */ \
778 "24: clfi %[R_TMP],0x10ffff\n\t" \
779 " jh 91f\n\t" /* ch > 0x10ffff is not allowed! */ \
780 /* Handle 4-byte UTF-8 char. */ \
781 "34: slgfi %[R_OUTLEN],4\n\t" \
782 " jl 90f \n\t" \
783 " llilf %[R_TMP3],0xf0808080\n\t" \
784 " risbgn %[R_TMP3],%[R_TMP],37,39,6\n\t" /* 1. byte. */ \
785 " risbgn %[R_TMP3],%[R_TMP],42,47,4\n\t" /* 2. byte. */ \
786 " risbgn %[R_TMP3],%[R_TMP],50,55,2\n\t" /* 3. byte. */ \
787 " risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 4. byte. */ \
788 " st %[R_TMP3],0(%[R_OUT])\n\t" \
789 " la %[R_IN],4(%[R_IN])\n\t" \
790 " la %[R_OUT],4(%[R_OUT])\n\t" \
791 " brctg %[R_TMP2],20b\n\t" \
792 " j 0b\n\t" /* Switch to vx-loop. */ \
793 "92: lghi %[R_RES],%[RES_IN_FULL]\n\t" \
794 " j 99f\n\t" \
795 "91: lghi %[R_RES],%[RES_IN_ILL]\n\t" \
796 " j 99f\n\t" \
797 "90: lghi %[R_RES],%[RES_OUT_FULL]\n\t" \
798 "99: \n\t" \
799 ".machine pop" \
800 : /* outputs */ [R_IN] "+a" (inptr) \
801 , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (outptr) \
802 , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp) \
803 , [R_TMP2] "=a" (tmp2), [R_TMP3] "=d" (tmp3) \
804 , [R_RES] "+d" (result) \
805 : /* inputs */ \
806 [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT) \
807 , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT) \
808 , [RES_IN_FULL] "i" (__GCONV_INCOMPLETE_INPUT) \
809 : /* clobber list */ "memory", "cc" \
810 ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17") \
811 ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19") \
812 ASM_CLOBBER_VR ("v20") ASM_CLOBBER_VR ("v21") \
813 ASM_CLOBBER_VR ("v22") ASM_CLOBBER_VR ("v23") \
814 ASM_CLOBBER_VR ("v24") \
815 ); \
816 if (__glibc_likely (inptr == inend) \
817 || result != __GCONV_ILLEGAL_INPUT) \
818 break; \
820 STANDARD_TO_LOOP_ERR_HANDLER (4); \
823 /* Generate loop-function with software routing. */
824 #define MIN_NEEDED_INPUT MIN_NEEDED_TO
825 #define MIN_NEEDED_OUTPUT MIN_NEEDED_FROM
826 #define MAX_NEEDED_OUTPUT MAX_NEEDED_FROM
827 #define LOOPFCT __to_utf8_loop_c
828 #define BODY BODY_TO_C
829 #define LOOP_NEED_FLAGS
830 #include <iconv/loop.c>
832 #if defined HAVE_S390_VX_ASM_SUPPORT
833 /* Generate loop-function with hardware vector and utf-convert instructions. */
834 # define MIN_NEEDED_INPUT MIN_NEEDED_TO
835 # define MIN_NEEDED_OUTPUT MIN_NEEDED_FROM
836 # define MAX_NEEDED_OUTPUT MAX_NEEDED_FROM
837 # define LOOPFCT __to_utf8_loop_vx
838 # define BODY BODY_TO_VX
839 # define LOOP_NEED_FLAGS
840 # include <iconv/loop.c>
841 #endif
843 /* Generate ifunc'ed loop function. */
844 __typeof(__to_utf8_loop_c)
845 __attribute__ ((ifunc ("__to_utf8_loop_resolver")))
846 __to_utf8_loop;
848 static void *
849 __to_utf8_loop_resolver (unsigned long int dl_hwcap)
851 #if defined HAVE_S390_VX_ASM_SUPPORT
852 if (dl_hwcap & HWCAP_S390_VX)
853 return __to_utf8_loop_vx;
854 else
855 #endif
856 return __to_utf8_loop_c;
859 strong_alias (__to_utf8_loop_c_single, __to_utf8_loop_single)
862 #include <iconv/skeleton.c>