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