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