Replace M_El with lit_e in libm-test.inc
[glibc.git] / sysdeps / s390 / utf8-utf16-z9.c
blobd3dc9bd97e072902b9b983fadccf51088b91aea2
1 /* Conversion between UTF-16 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 4
50 #define MIN_NEEDED_TO 2
51 #define MAX_NEEDED_TO 4
52 #define FROM_LOOP __from_utf8_loop
53 #define TO_LOOP __to_utf8_loop
54 #define FROM_DIRECTION (dir == from_utf8)
55 #define ONE_DIRECTION 0
58 /* UTF-16 big endian byte order mark. */
59 #define BOM_UTF16 0xfeff
61 /* Direction of the transformation. */
62 enum direction
64 illegal_dir,
65 to_utf8,
66 from_utf8
69 struct utf8_data
71 enum direction dir;
72 int emit_bom;
76 extern int gconv_init (struct __gconv_step *step);
77 int
78 gconv_init (struct __gconv_step *step)
80 /* Determine which direction. */
81 struct utf8_data *new_data;
82 enum direction dir = illegal_dir;
83 int emit_bom;
84 int result;
86 emit_bom = (__strcasecmp (step->__to_name, "UTF-16//") == 0);
88 if (__strcasecmp (step->__from_name, "ISO-10646/UTF8/") == 0
89 && (__strcasecmp (step->__to_name, "UTF-16//") == 0
90 || __strcasecmp (step->__to_name, "UTF-16BE//") == 0))
92 dir = from_utf8;
94 else if (__strcasecmp (step->__from_name, "UTF-16BE//") == 0
95 && __strcasecmp (step->__to_name, "ISO-10646/UTF8/") == 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 UTF-16 Byte Order Mark. */ \
188 if (__glibc_unlikely (outbuf + 2 > outend)) \
189 return __GCONV_FULL_OUTPUT; \
191 put16u (outbuf, BOM_UTF16); \
192 outbuf += 2; \
195 /* Conversion function from UTF-8 to UTF-16. */
196 #define BODY_FROM_HW(ASM) \
198 ASM; \
199 if (__glibc_likely (inptr == inend) \
200 || result == __GCONV_FULL_OUTPUT) \
201 break; \
203 int i; \
204 for (i = 1; inptr + i < inend && i < 5; ++i) \
205 if ((inptr[i] & 0xc0) != 0x80) \
206 break; \
208 if (__glibc_likely (inptr + i == inend \
209 && result == __GCONV_EMPTY_INPUT)) \
211 result = __GCONV_INCOMPLETE_INPUT; \
212 break; \
214 STANDARD_FROM_LOOP_ERR_HANDLER (i); \
217 #define BODY_FROM_ETF3EH BODY_FROM_HW (HARDWARE_CONVERT ("cu12 %0, %1, 1"))
219 #define HW_FROM_VX \
221 register const unsigned char* pInput asm ("8") = inptr; \
222 register size_t inlen asm ("9") = inend - inptr; \
223 register unsigned char* pOutput asm ("10") = outptr; \
224 register size_t outlen asm("11") = outend - outptr; \
225 unsigned long tmp, tmp2, tmp3; \
226 asm volatile (".machine push\n\t" \
227 ".machine \"z13\"\n\t" \
228 ".machinemode \"zarch_nohighgprs\"\n\t" \
229 " vrepib %%v30,0x7f\n\t" /* For compare > 0x7f. */ \
230 " vrepib %%v31,0x20\n\t" \
231 CONVERT_32BIT_SIZE_T ([R_INLEN]) \
232 CONVERT_32BIT_SIZE_T ([R_OUTLEN]) \
233 /* Loop which handles UTF-8 chars <=0x7f. */ \
234 "0: clgijl %[R_INLEN],16,20f\n\t" \
235 " clgijl %[R_OUTLEN],32,20f\n\t" \
236 "1: vl %%v16,0(%[R_IN])\n\t" \
237 " vstrcbs %%v17,%%v16,%%v30,%%v31\n\t" \
238 " jno 10f\n\t" /* Jump away if not all bytes are 1byte \
239 UTF8 chars. */ \
240 /* Enlarge to UTF-16. */ \
241 " vuplhb %%v18,%%v16\n\t" \
242 " la %[R_IN],16(%[R_IN])\n\t" \
243 " vupllb %%v19,%%v16\n\t" \
244 " aghi %[R_INLEN],-16\n\t" \
245 /* Store 32 bytes to buf_out. */ \
246 " vstm %%v18,%%v19,0(%[R_OUT])\n\t" \
247 " aghi %[R_OUTLEN],-32\n\t" \
248 " la %[R_OUT],32(%[R_OUT])\n\t" \
249 " clgijl %[R_INLEN],16,20f\n\t" \
250 " clgijl %[R_OUTLEN],32,20f\n\t" \
251 " j 1b\n\t" \
252 "10:\n\t" \
253 /* At least one byte is > 0x7f. \
254 Store the preceding 1-byte chars. */ \
255 " vlgvb %[R_TMP],%%v17,7\n\t" \
256 " sllk %[R_TMP2],%[R_TMP],1\n\t" /* Compute highest \
257 index to store. */ \
258 " llgfr %[R_TMP3],%[R_TMP2]\n\t" \
259 " ahi %[R_TMP2],-1\n\t" \
260 " jl 20f\n\t" \
261 " vuplhb %%v18,%%v16\n\t" \
262 " vstl %%v18,%[R_TMP2],0(%[R_OUT])\n\t" \
263 " ahi %[R_TMP2],-16\n\t" \
264 " jl 11f\n\t" \
265 " vupllb %%v19,%%v16\n\t" \
266 " vstl %%v19,%[R_TMP2],16(%[R_OUT])\n\t" \
267 "11: \n\t" /* Update pointers. */ \
268 " la %[R_IN],0(%[R_TMP],%[R_IN])\n\t" \
269 " slgr %[R_INLEN],%[R_TMP]\n\t" \
270 " la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t" \
271 " slgr %[R_OUTLEN],%[R_TMP3]\n\t" \
272 /* Handle multibyte utf8-char with convert instruction. */ \
273 "20: cu12 %[R_OUT],%[R_IN],1\n\t" \
274 " jo 0b\n\t" /* Try vector implemenation again. */ \
275 " lochil %[R_RES],%[RES_OUT_FULL]\n\t" /* cc == 1. */ \
276 " lochih %[R_RES],%[RES_IN_ILL]\n\t" /* cc == 2. */ \
277 ".machine pop" \
278 : /* outputs */ [R_IN] "+a" (pInput) \
279 , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (pOutput) \
280 , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp) \
281 , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3) \
282 , [R_RES] "+d" (result) \
283 : /* inputs */ \
284 [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT) \
285 , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT) \
286 : /* clobber list */ "memory", "cc" \
287 ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17") \
288 ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19") \
289 ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31") \
290 ); \
291 inptr = pInput; \
292 outptr = pOutput; \
294 #define BODY_FROM_VX BODY_FROM_HW (HW_FROM_VX)
297 /* The software implementation is based on the code in gconv_simple.c. */
298 #define BODY_FROM_C \
300 /* Next input byte. */ \
301 uint16_t ch = *inptr; \
303 if (__glibc_likely (ch < 0x80)) \
305 /* One byte sequence. */ \
306 ++inptr; \
308 else \
310 uint_fast32_t cnt; \
311 uint_fast32_t i; \
313 if (ch >= 0xc2 && ch < 0xe0) \
315 /* We expect two bytes. The first byte cannot be 0xc0 \
316 or 0xc1, otherwise the wide character could have been \
317 represented using a single byte. */ \
318 cnt = 2; \
319 ch &= 0x1f; \
321 else if (__glibc_likely ((ch & 0xf0) == 0xe0)) \
323 /* We expect three bytes. */ \
324 cnt = 3; \
325 ch &= 0x0f; \
327 else if (__glibc_likely ((ch & 0xf8) == 0xf0)) \
329 /* We expect four bytes. */ \
330 cnt = 4; \
331 ch &= 0x07; \
333 else \
335 /* Search the end of this ill-formed UTF-8 character. This \
336 is the next byte with (x & 0xc0) != 0x80. */ \
337 i = 0; \
338 do \
339 ++i; \
340 while (inptr + i < inend \
341 && (*(inptr + i) & 0xc0) == 0x80 \
342 && i < 5); \
344 errout: \
345 STANDARD_FROM_LOOP_ERR_HANDLER (i); \
348 if (__glibc_unlikely (inptr + cnt > inend)) \
350 /* We don't have enough input. But before we report \
351 that check that all the bytes are correct. */ \
352 for (i = 1; inptr + i < inend; ++i) \
353 if ((inptr[i] & 0xc0) != 0x80) \
354 break; \
356 if (__glibc_likely (inptr + i == inend)) \
358 result = __GCONV_INCOMPLETE_INPUT; \
359 break; \
362 goto errout; \
365 if (cnt == 4) \
367 /* For 4 byte UTF-8 chars two UTF-16 chars (high and \
368 low) are needed. */ \
369 uint16_t zabcd, high, low; \
371 if (__glibc_unlikely (outptr + 4 > outend)) \
373 /* Overflow in the output buffer. */ \
374 result = __GCONV_FULL_OUTPUT; \
375 break; \
378 /* Check if tail-bytes >= 0x80, < 0xc0. */ \
379 for (i = 1; i < cnt; ++i) \
381 if ((inptr[i] & 0xc0) != 0x80) \
382 /* This is an illegal encoding. */ \
383 goto errout; \
386 /* See Principles of Operations cu12. */ \
387 zabcd = (((inptr[0] & 0x7) << 2) | \
388 ((inptr[1] & 0x30) >> 4)) - 1; \
390 /* z-bit must be zero after subtracting 1. */ \
391 if (zabcd & 0x10) \
392 STANDARD_FROM_LOOP_ERR_HANDLER (4) \
394 high = (uint16_t)(0xd8 << 8); /* high surrogate id */ \
395 high |= zabcd << 6; /* abcd bits */ \
396 high |= (inptr[1] & 0xf) << 2; /* efgh bits */ \
397 high |= (inptr[2] & 0x30) >> 4; /* ij bits */ \
399 low = (uint16_t)(0xdc << 8); /* low surrogate id */ \
400 low |= ((uint16_t)inptr[2] & 0xc) << 6; /* kl bits */ \
401 low |= (inptr[2] & 0x3) << 6; /* mn bits */ \
402 low |= inptr[3] & 0x3f; /* opqrst bits */ \
404 put16 (outptr, high); \
405 outptr += 2; \
406 put16 (outptr, low); \
407 outptr += 2; \
408 inptr += 4; \
409 continue; \
411 else \
413 /* Read the possible remaining bytes. */ \
414 for (i = 1; i < cnt; ++i) \
416 uint16_t byte = inptr[i]; \
418 if ((byte & 0xc0) != 0x80) \
419 /* This is an illegal encoding. */ \
420 break; \
422 ch <<= 6; \
423 ch |= byte & 0x3f; \
426 /* If i < cnt, some trail byte was not >= 0x80, < 0xc0. \
427 If cnt > 2 and ch < 2^(5*cnt-4), the wide character ch could \
428 have been represented with fewer than cnt bytes. */ \
429 if (i < cnt || (cnt > 2 && (ch >> (5 * cnt - 4)) == 0) \
430 /* Do not accept UTF-16 surrogates. */ \
431 || (ch >= 0xd800 && ch <= 0xdfff)) \
433 /* This is an illegal encoding. */ \
434 goto errout; \
437 inptr += cnt; \
440 /* Now adjust the pointers and store the result. */ \
441 *((uint16_t *) outptr) = ch; \
442 outptr += sizeof (uint16_t); \
445 /* Generate loop-function with software implementation. */
446 #define MIN_NEEDED_INPUT MIN_NEEDED_FROM
447 #define MAX_NEEDED_INPUT MAX_NEEDED_FROM
448 #define MIN_NEEDED_OUTPUT MIN_NEEDED_TO
449 #define MAX_NEEDED_OUTPUT MAX_NEEDED_TO
450 #define LOOPFCT __from_utf8_loop_c
451 #define LOOP_NEED_FLAGS
452 #define BODY BODY_FROM_C
453 #include <iconv/loop.c>
455 /* Generate loop-function with hardware utf-convert instruction. */
456 #define MIN_NEEDED_INPUT MIN_NEEDED_FROM
457 #define MAX_NEEDED_INPUT MAX_NEEDED_FROM
458 #define MIN_NEEDED_OUTPUT MIN_NEEDED_TO
459 #define MAX_NEEDED_OUTPUT MAX_NEEDED_TO
460 #define LOOPFCT __from_utf8_loop_etf3eh
461 #define LOOP_NEED_FLAGS
462 #define BODY BODY_FROM_ETF3EH
463 #include <iconv/loop.c>
465 #if defined HAVE_S390_VX_ASM_SUPPORT
466 /* Generate loop-function with hardware vector and utf-convert instructions. */
467 # define MIN_NEEDED_INPUT MIN_NEEDED_FROM
468 # define MAX_NEEDED_INPUT MAX_NEEDED_FROM
469 # define MIN_NEEDED_OUTPUT MIN_NEEDED_TO
470 # define MAX_NEEDED_OUTPUT MAX_NEEDED_TO
471 # define LOOPFCT __from_utf8_loop_vx
472 # define LOOP_NEED_FLAGS
473 # define BODY BODY_FROM_VX
474 # include <iconv/loop.c>
475 #endif
478 /* Generate ifunc'ed loop function. */
479 __typeof(__from_utf8_loop_c)
480 __attribute__ ((ifunc ("__from_utf8_loop_resolver")))
481 __from_utf8_loop;
483 static void *
484 __from_utf8_loop_resolver (unsigned long int dl_hwcap)
486 #if defined HAVE_S390_VX_ASM_SUPPORT
487 if (dl_hwcap & HWCAP_S390_VX)
488 return __from_utf8_loop_vx;
489 else
490 #endif
491 if (dl_hwcap & HWCAP_S390_ZARCH && dl_hwcap & HWCAP_S390_HIGH_GPRS
492 && dl_hwcap & HWCAP_S390_ETF3EH)
493 return __from_utf8_loop_etf3eh;
494 else
495 return __from_utf8_loop_c;
498 strong_alias (__from_utf8_loop_c_single, __from_utf8_loop_single)
500 /* Conversion from UTF-16 to UTF-8. */
502 /* The software routine is based on the functionality of the S/390
503 hardware instruction (cu21) as described in the Principles of
504 Operation. */
505 #define BODY_TO_C \
507 uint16_t c = get16 (inptr); \
509 if (__glibc_likely (c <= 0x007f)) \
511 /* Single byte UTF-8 char. */ \
512 *outptr = c & 0xff; \
513 outptr++; \
515 else if (c >= 0x0080 && c <= 0x07ff) \
517 /* Two byte UTF-8 char. */ \
519 if (__glibc_unlikely (outptr + 2 > outend)) \
521 /* Overflow in the output buffer. */ \
522 result = __GCONV_FULL_OUTPUT; \
523 break; \
526 outptr[0] = 0xc0; \
527 outptr[0] |= c >> 6; \
529 outptr[1] = 0x80; \
530 outptr[1] |= c & 0x3f; \
532 outptr += 2; \
534 else if ((c >= 0x0800 && c <= 0xd7ff) || c > 0xdfff) \
536 /* Three byte UTF-8 char. */ \
538 if (__glibc_unlikely (outptr + 3 > outend)) \
540 /* Overflow in the output buffer. */ \
541 result = __GCONV_FULL_OUTPUT; \
542 break; \
544 outptr[0] = 0xe0; \
545 outptr[0] |= c >> 12; \
547 outptr[1] = 0x80; \
548 outptr[1] |= (c >> 6) & 0x3f; \
550 outptr[2] = 0x80; \
551 outptr[2] |= c & 0x3f; \
553 outptr += 3; \
555 else if (c >= 0xd800 && c <= 0xdbff) \
557 /* Four byte UTF-8 char. */ \
558 uint16_t low, uvwxy; \
560 if (__glibc_unlikely (outptr + 4 > outend)) \
562 /* Overflow in the output buffer. */ \
563 result = __GCONV_FULL_OUTPUT; \
564 break; \
566 if (__glibc_unlikely (inptr + 4 > inend)) \
568 result = __GCONV_INCOMPLETE_INPUT; \
569 break; \
572 inptr += 2; \
573 low = get16 (inptr); \
575 if ((low & 0xfc00) != 0xdc00) \
577 inptr -= 2; \
578 STANDARD_TO_LOOP_ERR_HANDLER (2); \
580 uvwxy = ((c >> 6) & 0xf) + 1; \
581 outptr[0] = 0xf0; \
582 outptr[0] |= uvwxy >> 2; \
584 outptr[1] = 0x80; \
585 outptr[1] |= (uvwxy << 4) & 0x30; \
586 outptr[1] |= (c >> 2) & 0x0f; \
588 outptr[2] = 0x80; \
589 outptr[2] |= (c & 0x03) << 4; \
590 outptr[2] |= (low >> 6) & 0x0f; \
592 outptr[3] = 0x80; \
593 outptr[3] |= low & 0x3f; \
595 outptr += 4; \
597 else \
599 STANDARD_TO_LOOP_ERR_HANDLER (2); \
601 inptr += 2; \
604 #define BODY_TO_VX \
606 size_t inlen = inend - inptr; \
607 size_t outlen = outend - outptr; \
608 unsigned long tmp, tmp2, tmp3; \
609 asm volatile (".machine push\n\t" \
610 ".machine \"z13\"\n\t" \
611 ".machinemode \"zarch_nohighgprs\"\n\t" \
612 /* Setup to check for values <= 0x7f. */ \
613 " larl %[R_TMP],9f\n\t" \
614 " vlm %%v30,%%v31,0(%[R_TMP])\n\t" \
615 CONVERT_32BIT_SIZE_T ([R_INLEN]) \
616 CONVERT_32BIT_SIZE_T ([R_OUTLEN]) \
617 /* Loop which handles UTF-16 chars <=0x7f. */ \
618 "0: clgijl %[R_INLEN],32,2f\n\t" \
619 " clgijl %[R_OUTLEN],16,2f\n\t" \
620 "1: vlm %%v16,%%v17,0(%[R_IN])\n\t" \
621 " lghi %[R_TMP2],0\n\t" \
622 /* Check for > 1byte UTF-8 chars. */ \
623 " vstrchs %%v19,%%v16,%%v30,%%v31\n\t" \
624 " jno 10f\n\t" /* Jump away if not all bytes are 1byte \
625 UTF8 chars. */ \
626 " vstrchs %%v19,%%v17,%%v30,%%v31\n\t" \
627 " jno 11f\n\t" /* Jump away if not all bytes are 1byte \
628 UTF8 chars. */ \
629 /* Shorten to UTF-8. */ \
630 " vpkh %%v18,%%v16,%%v17\n\t" \
631 " la %[R_IN],32(%[R_IN])\n\t" \
632 " aghi %[R_INLEN],-32\n\t" \
633 /* Store 16 bytes to buf_out. */ \
634 " vst %%v18,0(%[R_OUT])\n\t" \
635 " aghi %[R_OUTLEN],-16\n\t" \
636 " la %[R_OUT],16(%[R_OUT])\n\t" \
637 " clgijl %[R_INLEN],32,2f\n\t" \
638 " clgijl %[R_OUTLEN],16,2f\n\t" \
639 " j 1b\n\t" \
640 /* Setup to check for ch > 0x7f. (v30, v31) */ \
641 "9: .short 0x7f,0x7f,0x0,0x0,0x0,0x0,0x0,0x0\n\t" \
642 " .short 0x2000,0x2000,0x0,0x0,0x0,0x0,0x0,0x0\n\t" \
643 /* At least one byte is > 0x7f. \
644 Store the preceding 1-byte chars. */ \
645 "11: lghi %[R_TMP2],16\n\t" /* match was found in v17. */ \
646 "10:\n\t" \
647 " vlgvb %[R_TMP],%%v19,7\n\t" \
648 /* Shorten to UTF-8. */ \
649 " vpkh %%v18,%%v16,%%v17\n\t" \
650 " ar %[R_TMP],%[R_TMP2]\n\t" /* Number of in bytes. */ \
651 " srlg %[R_TMP3],%[R_TMP],1\n\t" /* Number of out bytes. */ \
652 " ahik %[R_TMP2],%[R_TMP3],-1\n\t" /* Highest index to store. */ \
653 " jl 13f\n\t" \
654 " vstl %%v18,%[R_TMP2],0(%[R_OUT])\n\t" \
655 /* Update pointers. */ \
656 " la %[R_IN],0(%[R_TMP],%[R_IN])\n\t" \
657 " slgr %[R_INLEN],%[R_TMP]\n\t" \
658 " la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t" \
659 " slgr %[R_OUTLEN],%[R_TMP3]\n\t" \
660 "13: \n\t" \
661 /* Calculate remaining uint16_t values in loaded vrs. */ \
662 " lghi %[R_TMP2],16\n\t" \
663 " slgr %[R_TMP2],%[R_TMP3]\n\t" \
664 " llh %[R_TMP],0(%[R_IN])\n\t" \
665 " aghi %[R_INLEN],-2\n\t" \
666 " j 22f\n\t" \
667 /* Handle remaining bytes. */ \
668 "2: \n\t" \
669 /* Zero, one or more bytes available? */ \
670 " clgfi %[R_INLEN],1\n\t" \
671 " locghie %[R_RES],%[RES_IN_FULL]\n\t" /* Only one byte. */ \
672 " jle 99f\n\t" /* End if less than two bytes. */ \
673 /* Calculate remaining uint16_t values in inptr. */ \
674 " srlg %[R_TMP2],%[R_INLEN],1\n\t" \
675 /* Handle multibyte utf8-char. */ \
676 "20: llh %[R_TMP],0(%[R_IN])\n\t" \
677 " aghi %[R_INLEN],-2\n\t" \
678 /* Test if ch is 1-byte UTF-8 char. */ \
679 "21: clijh %[R_TMP],0x7f,22f\n\t" \
680 /* Handle 1-byte UTF-8 char. */ \
681 "31: slgfi %[R_OUTLEN],1\n\t" \
682 " jl 90f \n\t" \
683 " stc %[R_TMP],0(%[R_OUT])\n\t" \
684 " la %[R_IN],2(%[R_IN])\n\t" \
685 " la %[R_OUT],1(%[R_OUT])\n\t" \
686 " brctg %[R_TMP2],20b\n\t" \
687 " j 0b\n\t" /* Switch to vx-loop. */ \
688 /* Test if ch is 2-byte UTF-8 char. */ \
689 "22: clfi %[R_TMP],0x7ff\n\t" \
690 " jh 23f\n\t" \
691 /* Handle 2-byte UTF-8 char. */ \
692 "32: slgfi %[R_OUTLEN],2\n\t" \
693 " jl 90f \n\t" \
694 " llill %[R_TMP3],0xc080\n\t" \
695 " la %[R_IN],2(%[R_IN])\n\t" \
696 " risbgn %[R_TMP3],%[R_TMP],51,55,2\n\t" /* 1. byte. */ \
697 " risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 2. byte. */ \
698 " sth %[R_TMP3],0(%[R_OUT])\n\t" \
699 " la %[R_OUT],2(%[R_OUT])\n\t" \
700 " brctg %[R_TMP2],20b\n\t" \
701 " j 0b\n\t" /* Switch to vx-loop. */ \
702 /* Test if ch is 3-byte UTF-8 char. */ \
703 "23: clfi %[R_TMP],0xd7ff\n\t" \
704 " jh 24f\n\t" \
705 /* Handle 3-byte UTF-8 char. */ \
706 "33: slgfi %[R_OUTLEN],3\n\t" \
707 " jl 90f \n\t" \
708 " llilf %[R_TMP3],0xe08080\n\t" \
709 " la %[R_IN],2(%[R_IN])\n\t" \
710 " risbgn %[R_TMP3],%[R_TMP],44,47,4\n\t" /* 1. byte. */ \
711 " risbgn %[R_TMP3],%[R_TMP],50,55,2\n\t" /* 2. byte. */ \
712 " risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 3. byte. */ \
713 " stcm %[R_TMP3],7,0(%[R_OUT])\n\t" \
714 " la %[R_OUT],3(%[R_OUT])\n\t" \
715 " brctg %[R_TMP2],20b\n\t" \
716 " j 0b\n\t" /* Switch to vx-loop. */ \
717 /* Test if ch is 4-byte UTF-8 char. */ \
718 "24: clfi %[R_TMP],0xdfff\n\t" \
719 " jh 33b\n\t" /* Handle this 3-byte UTF-8 char. */ \
720 " clfi %[R_TMP],0xdbff\n\t" \
721 " locghih %[R_RES],%[RES_IN_ILL]\n\t" \
722 " jh 99f\n\t" /* Jump away if this is a low surrogate \
723 without a preceding high surrogate. */ \
724 /* Handle 4-byte UTF-8 char. */ \
725 "34: slgfi %[R_OUTLEN],4\n\t" \
726 " jl 90f \n\t" \
727 " slgfi %[R_INLEN],2\n\t" \
728 " locghil %[R_RES],%[RES_IN_FULL]\n\t" \
729 " jl 99f\n\t" /* Jump away if low surrogate is missing. */ \
730 " llilf %[R_TMP3],0xf0808080\n\t" \
731 " aghi %[R_TMP],0x40\n\t" \
732 " risbgn %[R_TMP3],%[R_TMP],37,39,16\n\t" /* 1. byte: uvw */ \
733 " risbgn %[R_TMP3],%[R_TMP],42,43,14\n\t" /* 2. byte: xy */ \
734 " risbgn %[R_TMP3],%[R_TMP],44,47,14\n\t" /* 2. byte: efgh */ \
735 " risbgn %[R_TMP3],%[R_TMP],50,51,12\n\t" /* 3. byte: ij */ \
736 " llh %[R_TMP],2(%[R_IN])\n\t" /* Load low surrogate. */ \
737 " risbgn %[R_TMP3],%[R_TMP],52,55,2\n\t" /* 3. byte: klmn */ \
738 " risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 4. byte: opqrst */ \
739 " nilf %[R_TMP],0xfc00\n\t" \
740 " clfi %[R_TMP],0xdc00\n\t" /* Check if it starts with 0xdc00. */ \
741 " locghine %[R_RES],%[RES_IN_ILL]\n\t" \
742 " jne 99f\n\t" /* Jump away if low surrogate is invalid. */ \
743 " st %[R_TMP3],0(%[R_OUT])\n\t" \
744 " la %[R_IN],4(%[R_IN])\n\t" \
745 " la %[R_OUT],4(%[R_OUT])\n\t" \
746 " aghi %[R_TMP2],-2\n\t" \
747 " jh 20b\n\t" \
748 " j 0b\n\t" /* Switch to vx-loop. */ \
749 /* Exit with __GCONV_FULL_OUTPUT. */ \
750 "90: lghi %[R_RES],%[RES_OUT_FULL]\n\t" \
751 "99: \n\t" \
752 ".machine pop" \
753 : /* outputs */ [R_IN] "+a" (inptr) \
754 , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (outptr) \
755 , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp) \
756 , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3) \
757 , [R_RES] "+d" (result) \
758 : /* inputs */ \
759 [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT) \
760 , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT) \
761 , [RES_IN_FULL] "i" (__GCONV_INCOMPLETE_INPUT) \
762 : /* clobber list */ "memory", "cc" \
763 ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17") \
764 ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19") \
765 ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31") \
766 ); \
767 if (__glibc_likely (inptr == inend) \
768 || result != __GCONV_ILLEGAL_INPUT) \
769 break; \
771 STANDARD_TO_LOOP_ERR_HANDLER (2); \
774 /* Generate loop-function with software implementation. */
775 #define MIN_NEEDED_INPUT MIN_NEEDED_TO
776 #define MAX_NEEDED_INPUT MAX_NEEDED_TO
777 #define MIN_NEEDED_OUTPUT MIN_NEEDED_FROM
778 #define MAX_NEEDED_OUTPUT MAX_NEEDED_FROM
779 #if defined HAVE_S390_VX_ASM_SUPPORT
780 # define LOOPFCT __to_utf8_loop_c
781 # define BODY BODY_TO_C
782 # define LOOP_NEED_FLAGS
783 # include <iconv/loop.c>
785 /* Generate loop-function with software implementation. */
786 # define MIN_NEEDED_INPUT MIN_NEEDED_TO
787 # define MAX_NEEDED_INPUT MAX_NEEDED_TO
788 # define MIN_NEEDED_OUTPUT MIN_NEEDED_FROM
789 # define MAX_NEEDED_OUTPUT MAX_NEEDED_FROM
790 # define LOOPFCT __to_utf8_loop_vx
791 # define BODY BODY_TO_VX
792 # define LOOP_NEED_FLAGS
793 # include <iconv/loop.c>
795 /* Generate ifunc'ed loop function. */
796 __typeof(__to_utf8_loop_c)
797 __attribute__ ((ifunc ("__to_utf8_loop_resolver")))
798 __to_utf8_loop;
800 static void *
801 __to_utf8_loop_resolver (unsigned long int dl_hwcap)
803 if (dl_hwcap & HWCAP_S390_VX)
804 return __to_utf8_loop_vx;
805 else
806 return __to_utf8_loop_c;
809 strong_alias (__to_utf8_loop_c_single, __to_utf8_loop_single)
811 #else
812 # define LOOPFCT TO_LOOP
813 # define BODY BODY_TO_C
814 # define LOOP_NEED_FLAGS
815 # include <iconv/loop.c>
816 #endif /* !HAVE_S390_VX_ASM_SUPPORT */
818 #include <iconv/skeleton.c>