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