]>
Commit | Line | Data |
---|---|---|
f957edde AK |
1 | /* Conversion between UTF-16 and UTF-32 BE/internal. |
2 | ||
3 | This module uses the Z9-109 variants of the Convert Unicode | |
4 | instructions. | |
6d7e8eda | 5 | Copyright (C) 1997-2023 Free Software Foundation, Inc. |
f957edde | 6 | |
f957edde AK |
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 | |
59ba27a6 | 18 | License along with the GNU C Library; if not, see |
5a82c748 | 19 | <https://www.gnu.org/licenses/>. */ |
f957edde AK |
20 | |
21 | #include <dlfcn.h> | |
22 | #include <stdint.h> | |
23 | #include <unistd.h> | |
f957edde | 24 | #include <gconv.h> |
85286aaf SL |
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 | #define HAVE_FROM_C 1 | |
30 | #define FROM_LOOP_DEFAULT FROM_LOOP_C | |
31 | #define HAVE_TO_C 1 | |
32 | #define TO_LOOP_DEFAULT TO_LOOP_C | |
33 | ||
34 | #if defined HAVE_S390_VX_ASM_SUPPORT && defined USE_MULTIARCH | |
35 | # define HAVE_FROM_VX 1 | |
f0137ff3 | 36 | # define HAVE_FROM_VX_CU 1 |
85286aaf | 37 | # define HAVE_TO_VX 1 |
593e4da1 | 38 | # define HAVE_TO_VX_CU 1 |
85286aaf SL |
39 | #else |
40 | # define HAVE_FROM_VX 0 | |
f0137ff3 | 41 | # define HAVE_FROM_VX_CU 0 |
85286aaf | 42 | # define HAVE_TO_VX 0 |
593e4da1 | 43 | # define HAVE_TO_VX_CU 0 |
85286aaf | 44 | #endif |
f957edde | 45 | |
6896776c SL |
46 | #if defined HAVE_S390_VX_GCC_SUPPORT |
47 | # define ASM_CLOBBER_VR(NR) , NR | |
48 | #else | |
49 | # define ASM_CLOBBER_VR(NR) | |
50 | #endif | |
51 | ||
ee518b70 SL |
52 | #if defined __s390x__ |
53 | # define CONVERT_32BIT_SIZE_T(REG) | |
54 | #else | |
55 | # define CONVERT_32BIT_SIZE_T(REG) "llgfr %" #REG ",%" #REG "\n\t" | |
56 | #endif | |
57 | ||
f957edde AK |
58 | /* UTF-32 big endian byte order mark. */ |
59 | #define BOM_UTF32 0x0000feffu | |
60 | ||
61 | /* UTF-16 big endian byte order mark. */ | |
6896776c | 62 | #define BOM_UTF16 0xfeff |
f957edde AK |
63 | |
64 | #define DEFINE_INIT 0 | |
65 | #define DEFINE_FINI 0 | |
66 | #define MIN_NEEDED_FROM 2 | |
67 | #define MAX_NEEDED_FROM 4 | |
68 | #define MIN_NEEDED_TO 4 | |
85286aaf SL |
69 | #define FROM_LOOP FROM_LOOP_DEFAULT |
70 | #define TO_LOOP TO_LOOP_DEFAULT | |
f957edde | 71 | #define FROM_DIRECTION (dir == from_utf16) |
f349489e | 72 | #define ONE_DIRECTION 0 |
f957edde AK |
73 | |
74 | /* Direction of the transformation. */ | |
75 | enum direction | |
76 | { | |
77 | illegal_dir, | |
78 | to_utf16, | |
79 | from_utf16 | |
80 | }; | |
81 | ||
82 | struct utf16_data | |
83 | { | |
84 | enum direction dir; | |
85 | int emit_bom; | |
86 | }; | |
87 | ||
88 | ||
89 | extern int gconv_init (struct __gconv_step *step); | |
90 | int | |
91 | gconv_init (struct __gconv_step *step) | |
92 | { | |
93 | /* Determine which direction. */ | |
94 | struct utf16_data *new_data; | |
95 | enum direction dir = illegal_dir; | |
96 | int emit_bom; | |
97 | int result; | |
98 | ||
99 | emit_bom = (__strcasecmp (step->__to_name, "UTF-32//") == 0 | |
100 | || __strcasecmp (step->__to_name, "UTF-16//") == 0); | |
101 | ||
102 | if (__strcasecmp (step->__from_name, "UTF-16BE//") == 0 | |
103 | && (__strcasecmp (step->__to_name, "UTF-32//") == 0 | |
104 | || __strcasecmp (step->__to_name, "UTF-32BE//") == 0 | |
89749d19 | 105 | || __strcasecmp (step->__to_name, "INTERNAL") == 0)) |
f957edde AK |
106 | { |
107 | dir = from_utf16; | |
108 | } | |
109 | else if ((__strcasecmp (step->__to_name, "UTF-16//") == 0 | |
110 | || __strcasecmp (step->__to_name, "UTF-16BE//") == 0) | |
111 | && (__strcasecmp (step->__from_name, "UTF-32BE//") == 0 | |
112 | || __strcasecmp (step->__from_name, "INTERNAL") == 0)) | |
113 | { | |
114 | dir = to_utf16; | |
115 | } | |
116 | ||
117 | result = __GCONV_NOCONV; | |
118 | if (dir != illegal_dir) | |
119 | { | |
120 | new_data = (struct utf16_data *) malloc (sizeof (struct utf16_data)); | |
121 | ||
122 | result = __GCONV_NOMEM; | |
123 | if (new_data != NULL) | |
124 | { | |
125 | new_data->dir = dir; | |
126 | new_data->emit_bom = emit_bom; | |
127 | step->__data = new_data; | |
128 | ||
129 | if (dir == from_utf16) | |
130 | { | |
131 | step->__min_needed_from = MIN_NEEDED_FROM; | |
132 | step->__max_needed_from = MIN_NEEDED_FROM; | |
133 | step->__min_needed_to = MIN_NEEDED_TO; | |
134 | step->__max_needed_to = MIN_NEEDED_TO; | |
135 | } | |
136 | else | |
137 | { | |
138 | step->__min_needed_from = MIN_NEEDED_TO; | |
139 | step->__max_needed_from = MIN_NEEDED_TO; | |
140 | step->__min_needed_to = MIN_NEEDED_FROM; | |
141 | step->__max_needed_to = MIN_NEEDED_FROM; | |
142 | } | |
143 | ||
144 | step->__stateful = 0; | |
145 | ||
146 | result = __GCONV_OK; | |
147 | } | |
148 | } | |
149 | ||
150 | return result; | |
151 | } | |
152 | ||
153 | ||
154 | extern void gconv_end (struct __gconv_step *data); | |
155 | void | |
156 | gconv_end (struct __gconv_step *data) | |
157 | { | |
158 | free (data->__data); | |
159 | } | |
160 | ||
6896776c SL |
161 | #define PREPARE_LOOP \ |
162 | enum direction dir = ((struct utf16_data *) step->__data)->dir; \ | |
163 | int emit_bom = ((struct utf16_data *) step->__data)->emit_bom; \ | |
164 | \ | |
165 | if (emit_bom && !data->__internal_use \ | |
166 | && data->__invocation_counter == 0) \ | |
167 | { \ | |
168 | if (dir == to_utf16) \ | |
169 | { \ | |
170 | /* Emit the UTF-16 Byte Order Mark. */ \ | |
171 | if (__glibc_unlikely (outbuf + 2 > outend)) \ | |
172 | return __GCONV_FULL_OUTPUT; \ | |
173 | \ | |
174 | put16u (outbuf, BOM_UTF16); \ | |
175 | outbuf += 2; \ | |
176 | } \ | |
177 | else \ | |
178 | { \ | |
179 | /* Emit the UTF-32 Byte Order Mark. */ \ | |
180 | if (__glibc_unlikely (outbuf + 4 > outend)) \ | |
181 | return __GCONV_FULL_OUTPUT; \ | |
182 | \ | |
183 | put32u (outbuf, BOM_UTF32); \ | |
184 | outbuf += 4; \ | |
185 | } \ | |
186 | } | |
187 | ||
f957edde AK |
188 | /* Conversion function from UTF-16 to UTF-32 internal/BE. */ |
189 | ||
85286aaf | 190 | #if HAVE_FROM_C == 1 |
f957edde AK |
191 | /* The software routine is copied from utf-16.c (minus bytes |
192 | swapping). */ | |
85286aaf | 193 | # define BODY_FROM_C \ |
f957edde | 194 | { \ |
f957edde AK |
195 | uint16_t u1 = get16 (inptr); \ |
196 | \ | |
197 | if (__builtin_expect (u1 < 0xd800, 1) || u1 > 0xdfff) \ | |
198 | { \ | |
199 | /* No surrogate. */ \ | |
200 | put32 (outptr, u1); \ | |
201 | inptr += 2; \ | |
202 | } \ | |
203 | else \ | |
204 | { \ | |
6896776c | 205 | /* An isolated low-surrogate was found. This has to be \ |
a3dc4658 | 206 | considered ill-formed. */ \ |
6896776c | 207 | if (__glibc_unlikely (u1 >= 0xdc00)) \ |
a3dc4658 AK |
208 | { \ |
209 | STANDARD_FROM_LOOP_ERR_HANDLER (2); \ | |
210 | } \ | |
f957edde AK |
211 | /* It's a surrogate character. At least the first word says \ |
212 | it is. */ \ | |
6896776c | 213 | if (__glibc_unlikely (inptr + 4 > inend)) \ |
f957edde AK |
214 | { \ |
215 | /* We don't have enough input for another complete input \ | |
216 | character. */ \ | |
217 | result = __GCONV_INCOMPLETE_INPUT; \ | |
218 | break; \ | |
219 | } \ | |
220 | \ | |
221 | inptr += 2; \ | |
222 | uint16_t u2 = get16 (inptr); \ | |
223 | if (__builtin_expect (u2 < 0xdc00, 0) \ | |
224 | || __builtin_expect (u2 > 0xdfff, 0)) \ | |
225 | { \ | |
226 | /* This is no valid second word for a surrogate. */ \ | |
227 | inptr -= 2; \ | |
228 | STANDARD_FROM_LOOP_ERR_HANDLER (2); \ | |
229 | } \ | |
230 | \ | |
231 | put32 (outptr, ((u1 - 0xd7c0) << 10) + (u2 - 0xdc00)); \ | |
232 | inptr += 2; \ | |
233 | } \ | |
234 | outptr += 4; \ | |
235 | } | |
6896776c | 236 | |
85286aaf SL |
237 | |
238 | /* Generate loop-function with software routing. */ | |
239 | # define MIN_NEEDED_INPUT MIN_NEEDED_FROM | |
240 | # define MAX_NEEDED_INPUT MAX_NEEDED_FROM | |
241 | # define MIN_NEEDED_OUTPUT MIN_NEEDED_TO | |
242 | # define FROM_LOOP_C __from_utf16_loop_c | |
243 | # define LOOPFCT FROM_LOOP_C | |
244 | # define LOOP_NEED_FLAGS | |
245 | # define BODY BODY_FROM_C | |
246 | # include <iconv/loop.c> | |
247 | #else | |
248 | # define FROM_LOOP_C NULL | |
249 | #endif /* HAVE_FROM_C != 1 */ | |
250 | ||
251 | #if HAVE_FROM_VX == 1 | |
252 | # define BODY_FROM_VX \ | |
6896776c SL |
253 | { \ |
254 | size_t inlen = inend - inptr; \ | |
255 | size_t outlen = outend - outptr; \ | |
256 | unsigned long tmp, tmp2, tmp3; \ | |
257 | asm volatile (".machine push\n\t" \ | |
258 | ".machine \"z13\"\n\t" \ | |
259 | ".machinemode \"zarch_nohighgprs\"\n\t" \ | |
260 | /* Setup to check for surrogates. */ \ | |
261 | " larl %[R_TMP],9f\n\t" \ | |
262 | " vlm %%v30,%%v31,0(%[R_TMP])\n\t" \ | |
ee518b70 SL |
263 | CONVERT_32BIT_SIZE_T ([R_INLEN]) \ |
264 | CONVERT_32BIT_SIZE_T ([R_OUTLEN]) \ | |
6896776c SL |
265 | /* Loop which handles UTF-16 chars <0xd800, >0xdfff. */ \ |
266 | "0: clgijl %[R_INLEN],16,2f\n\t" \ | |
267 | " clgijl %[R_OUTLEN],32,2f\n\t" \ | |
268 | "1: vl %%v16,0(%[R_IN])\n\t" \ | |
269 | /* Check for surrogate chars. */ \ | |
270 | " vstrchs %%v19,%%v16,%%v30,%%v31\n\t" \ | |
271 | " jno 10f\n\t" \ | |
272 | /* Enlarge to UTF-32. */ \ | |
273 | " vuplhh %%v17,%%v16\n\t" \ | |
274 | " la %[R_IN],16(%[R_IN])\n\t" \ | |
275 | " vupllh %%v18,%%v16\n\t" \ | |
276 | " aghi %[R_INLEN],-16\n\t" \ | |
277 | /* Store 32 bytes to buf_out. */ \ | |
278 | " vstm %%v17,%%v18,0(%[R_OUT])\n\t" \ | |
279 | " aghi %[R_OUTLEN],-32\n\t" \ | |
280 | " la %[R_OUT],32(%[R_OUT])\n\t" \ | |
281 | " clgijl %[R_INLEN],16,2f\n\t" \ | |
282 | " clgijl %[R_OUTLEN],32,2f\n\t" \ | |
283 | " j 1b\n\t" \ | |
284 | /* Setup to check for ch >= 0xd800 && ch <= 0xdfff. (v30, v31) */ \ | |
285 | "9: .short 0xd800,0xdfff,0x0,0x0,0x0,0x0,0x0,0x0\n\t" \ | |
286 | " .short 0xa000,0xc000,0x0,0x0,0x0,0x0,0x0,0x0\n\t" \ | |
85286aaf | 287 | /* At least one uint16_t is in range of surrogates. \ |
6896776c SL |
288 | Store the preceding chars. */ \ |
289 | "10: vlgvb %[R_TMP],%%v19,7\n\t" \ | |
290 | " vuplhh %%v17,%%v16\n\t" \ | |
291 | " sllg %[R_TMP3],%[R_TMP],1\n\t" /* Number of out bytes. */ \ | |
292 | " ahik %[R_TMP2],%[R_TMP3],-1\n\t" /* Highest index to store. */ \ | |
293 | " jl 12f\n\t" \ | |
294 | " vstl %%v17,%[R_TMP2],0(%[R_OUT])\n\t" \ | |
295 | " vupllh %%v18,%%v16\n\t" \ | |
296 | " ahi %[R_TMP2],-16\n\t" \ | |
297 | " jl 11f\n\t" \ | |
298 | " vstl %%v18,%[R_TMP2],16(%[R_OUT])\n\t" \ | |
299 | "11: \n\t" /* Update pointers. */ \ | |
300 | " la %[R_IN],0(%[R_TMP],%[R_IN])\n\t" \ | |
301 | " slgr %[R_INLEN],%[R_TMP]\n\t" \ | |
302 | " la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t" \ | |
303 | " slgr %[R_OUTLEN],%[R_TMP3]\n\t" \ | |
304 | /* Calculate remaining uint16_t values in loaded vrs. */ \ | |
305 | "12: lghi %[R_TMP2],16\n\t" \ | |
a42a95c4 | 306 | " slgr %[R_TMP2],%[R_TMP]\n\t" \ |
6896776c SL |
307 | " srl %[R_TMP2],1\n\t" \ |
308 | " llh %[R_TMP],0(%[R_IN])\n\t" \ | |
309 | " aghi %[R_OUTLEN],-4\n\t" \ | |
310 | " j 16f\n\t" \ | |
311 | /* Handle remaining bytes. */ \ | |
312 | "2: \n\t" \ | |
313 | /* Zero, one or more bytes available? */ \ | |
314 | " clgfi %[R_INLEN],1\n\t" \ | |
315 | " je 97f\n\t" /* Only one byte available. */ \ | |
316 | " jl 99f\n\t" /* End if no bytes available. */ \ | |
317 | /* Calculate remaining uint16_t values in inptr. */ \ | |
318 | " srlg %[R_TMP2],%[R_INLEN],1\n\t" \ | |
319 | /* Handle remaining uint16_t values. */ \ | |
320 | "13: llh %[R_TMP],0(%[R_IN])\n\t" \ | |
321 | " slgfi %[R_OUTLEN],4\n\t" \ | |
322 | " jl 96f \n\t" \ | |
323 | " clfi %[R_TMP],0xd800\n\t" \ | |
324 | " jhe 15f\n\t" \ | |
325 | "14: st %[R_TMP],0(%[R_OUT])\n\t" \ | |
326 | " la %[R_IN],2(%[R_IN])\n\t" \ | |
327 | " aghi %[R_INLEN],-2\n\t" \ | |
328 | " la %[R_OUT],4(%[R_OUT])\n\t" \ | |
329 | " brctg %[R_TMP2],13b\n\t" \ | |
330 | " j 0b\n\t" /* Switch to vx-loop. */ \ | |
331 | /* Handle UTF-16 surrogate pair. */ \ | |
332 | "15: clfi %[R_TMP],0xdfff\n\t" \ | |
333 | " jh 14b\n\t" /* Jump away if ch > 0xdfff. */ \ | |
334 | "16: clfi %[R_TMP],0xdc00\n\t" \ | |
335 | " jhe 98f\n\t" /* Jump away in case of low-surrogate. */ \ | |
336 | " slgfi %[R_INLEN],4\n\t" \ | |
337 | " jl 97f\n\t" /* Big enough input? */ \ | |
338 | " llh %[R_TMP3],2(%[R_IN])\n\t" /* Load low surrogate. */ \ | |
339 | " slfi %[R_TMP],0xd7c0\n\t" \ | |
340 | " sll %[R_TMP],10\n\t" \ | |
341 | " risbgn %[R_TMP],%[R_TMP3],54,63,0\n\t" /* Insert klmnopqrst. */ \ | |
342 | " nilf %[R_TMP3],0xfc00\n\t" \ | |
343 | " clfi %[R_TMP3],0xdc00\n\t" /* Check if it starts with 0xdc00. */ \ | |
344 | " jne 98f\n\t" \ | |
345 | " st %[R_TMP],0(%[R_OUT])\n\t" \ | |
346 | " la %[R_IN],4(%[R_IN])\n\t" \ | |
347 | " la %[R_OUT],4(%[R_OUT])\n\t" \ | |
348 | " aghi %[R_TMP2],-2\n\t" \ | |
349 | " jh 13b\n\t" /* Handle remaining uint16_t values. */ \ | |
350 | " j 0b\n\t" /* Switch to vx-loop. */ \ | |
351 | "96: \n\t" /* Return full output. */ \ | |
352 | " lghi %[R_RES],%[RES_OUT_FULL]\n\t" \ | |
353 | " j 99f\n\t" \ | |
354 | "97: \n\t" /* Return incomplete input. */ \ | |
355 | " lghi %[R_RES],%[RES_IN_FULL]\n\t" \ | |
356 | " j 99f\n\t" \ | |
357 | "98:\n\t" /* Return Illegal character. */ \ | |
358 | " lghi %[R_RES],%[RES_IN_ILL]\n\t" \ | |
359 | "99:\n\t" \ | |
360 | ".machine pop" \ | |
361 | : /* outputs */ [R_IN] "+a" (inptr) \ | |
362 | , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (outptr) \ | |
363 | , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp) \ | |
364 | , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3) \ | |
365 | , [R_RES] "+d" (result) \ | |
366 | : /* inputs */ \ | |
367 | [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT) \ | |
368 | , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT) \ | |
369 | , [RES_IN_FULL] "i" (__GCONV_INCOMPLETE_INPUT) \ | |
370 | : /* clobber list */ "memory", "cc" \ | |
371 | ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17") \ | |
372 | ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19") \ | |
373 | ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31") \ | |
374 | ); \ | |
375 | if (__glibc_likely (inptr == inend) \ | |
376 | || result != __GCONV_ILLEGAL_INPUT) \ | |
377 | break; \ | |
378 | \ | |
379 | STANDARD_FROM_LOOP_ERR_HANDLER (2); \ | |
380 | } | |
381 | ||
6896776c SL |
382 | /* Generate loop-function with hardware vector instructions. */ |
383 | # define MIN_NEEDED_INPUT MIN_NEEDED_FROM | |
384 | # define MAX_NEEDED_INPUT MAX_NEEDED_FROM | |
385 | # define MIN_NEEDED_OUTPUT MIN_NEEDED_TO | |
85286aaf SL |
386 | # define FROM_LOOP_VX __from_utf16_loop_vx |
387 | # define LOOPFCT FROM_LOOP_VX | |
6896776c SL |
388 | # define LOOP_NEED_FLAGS |
389 | # define BODY BODY_FROM_VX | |
390 | # include <iconv/loop.c> | |
6896776c | 391 | #else |
85286aaf SL |
392 | # define FROM_LOOP_VX NULL |
393 | #endif /* HAVE_FROM_VX != 1 */ | |
394 | ||
f0137ff3 SL |
395 | #if HAVE_FROM_VX_CU == 1 |
396 | #define BODY_FROM_VX_CU \ | |
397 | { \ | |
398 | register const unsigned char* pInput asm ("8") = inptr; \ | |
399 | register size_t inlen asm ("9") = inend - inptr; \ | |
400 | register unsigned char* pOutput asm ("10") = outptr; \ | |
401 | register size_t outlen asm ("11") = outend - outptr; \ | |
402 | unsigned long tmp, tmp2, tmp3; \ | |
403 | asm volatile (".machine push\n\t" \ | |
404 | ".machine \"z13\"\n\t" \ | |
405 | ".machinemode \"zarch_nohighgprs\"\n\t" \ | |
406 | /* Setup to check for surrogates. */ \ | |
407 | " larl %[R_TMP],9f\n\t" \ | |
408 | " vlm %%v30,%%v31,0(%[R_TMP])\n\t" \ | |
409 | CONVERT_32BIT_SIZE_T ([R_INLEN]) \ | |
410 | CONVERT_32BIT_SIZE_T ([R_OUTLEN]) \ | |
411 | /* Loop which handles UTF-16 chars <0xd800, >0xdfff. */ \ | |
412 | "0: clgijl %[R_INLEN],16,20f\n\t" \ | |
413 | " clgijl %[R_OUTLEN],32,20f\n\t" \ | |
414 | "1: vl %%v16,0(%[R_IN])\n\t" \ | |
415 | /* Check for surrogate chars. */ \ | |
416 | " vstrchs %%v19,%%v16,%%v30,%%v31\n\t" \ | |
417 | " jno 10f\n\t" \ | |
418 | /* Enlarge to UTF-32. */ \ | |
419 | " vuplhh %%v17,%%v16\n\t" \ | |
420 | " la %[R_IN],16(%[R_IN])\n\t" \ | |
421 | " vupllh %%v18,%%v16\n\t" \ | |
422 | " aghi %[R_INLEN],-16\n\t" \ | |
423 | /* Store 32 bytes to buf_out. */ \ | |
424 | " vstm %%v17,%%v18,0(%[R_OUT])\n\t" \ | |
425 | " aghi %[R_OUTLEN],-32\n\t" \ | |
426 | " la %[R_OUT],32(%[R_OUT])\n\t" \ | |
427 | " clgijl %[R_INLEN],16,20f\n\t" \ | |
428 | " clgijl %[R_OUTLEN],32,20f\n\t" \ | |
429 | " j 1b\n\t" \ | |
430 | /* Setup to check for ch >= 0xd800 && ch <= 0xdfff. (v30, v31) */ \ | |
431 | "9: .short 0xd800,0xdfff,0x0,0x0,0x0,0x0,0x0,0x0\n\t" \ | |
432 | " .short 0xa000,0xc000,0x0,0x0,0x0,0x0,0x0,0x0\n\t" \ | |
433 | /* At least one uint16_t is in range of surrogates. \ | |
434 | Store the preceding chars. */ \ | |
435 | "10: vlgvb %[R_TMP],%%v19,7\n\t" \ | |
436 | " vuplhh %%v17,%%v16\n\t" \ | |
437 | " sllg %[R_TMP3],%[R_TMP],1\n\t" /* Number of out bytes. */ \ | |
438 | " ahik %[R_TMP2],%[R_TMP3],-1\n\t" /* Highest index to store. */ \ | |
439 | " jl 20f\n\t" \ | |
440 | " vstl %%v17,%[R_TMP2],0(%[R_OUT])\n\t" \ | |
441 | " vupllh %%v18,%%v16\n\t" \ | |
442 | " ahi %[R_TMP2],-16\n\t" \ | |
443 | " jl 11f\n\t" \ | |
444 | " vstl %%v18,%[R_TMP2],16(%[R_OUT])\n\t" \ | |
445 | "11: \n\t" /* Update pointers. */ \ | |
446 | " la %[R_IN],0(%[R_TMP],%[R_IN])\n\t" \ | |
447 | " slgr %[R_INLEN],%[R_TMP]\n\t" \ | |
448 | " la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t" \ | |
449 | " slgr %[R_OUTLEN],%[R_TMP3]\n\t" \ | |
450 | /* Handles UTF16 surrogates with convert instruction. */ \ | |
451 | "20: cu24 %[R_OUT],%[R_IN],1\n\t" \ | |
452 | " jo 0b\n\t" /* Try vector implemenation again. */ \ | |
453 | " lochil %[R_RES],%[RES_OUT_FULL]\n\t" /* cc == 1. */ \ | |
454 | " lochih %[R_RES],%[RES_IN_ILL]\n\t" /* cc == 2. */ \ | |
455 | ".machine pop" \ | |
456 | : /* outputs */ [R_IN] "+a" (pInput) \ | |
457 | , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (pOutput) \ | |
458 | , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp) \ | |
459 | , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3) \ | |
460 | , [R_RES] "+d" (result) \ | |
461 | : /* inputs */ \ | |
462 | [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT) \ | |
463 | , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT) \ | |
464 | : /* clobber list */ "memory", "cc" \ | |
465 | ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17") \ | |
466 | ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19") \ | |
467 | ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31") \ | |
468 | ); \ | |
469 | inptr = pInput; \ | |
470 | outptr = pOutput; \ | |
471 | \ | |
472 | if (__glibc_likely (inlen == 0) \ | |
473 | || result == __GCONV_FULL_OUTPUT) \ | |
474 | break; \ | |
475 | if (inlen == 1) \ | |
476 | { \ | |
477 | /* Input does not contain a complete utf16 character. */ \ | |
478 | result = __GCONV_INCOMPLETE_INPUT; \ | |
479 | break; \ | |
480 | } \ | |
481 | else if (result != __GCONV_ILLEGAL_INPUT) \ | |
482 | { \ | |
483 | /* Input is >= 2 and < 4 bytes (as cu24 would have processed \ | |
484 | a possible next utf16 character) and not illegal. \ | |
485 | => we have a single high surrogate at end of input. */ \ | |
486 | result = __GCONV_INCOMPLETE_INPUT; \ | |
487 | break; \ | |
488 | } \ | |
489 | \ | |
490 | STANDARD_FROM_LOOP_ERR_HANDLER (2); \ | |
491 | } | |
492 | ||
493 | /* Generate loop-function with hardware vector and utf-convert instructions. */ | |
494 | # define MIN_NEEDED_INPUT MIN_NEEDED_FROM | |
495 | # define MAX_NEEDED_INPUT MAX_NEEDED_FROM | |
496 | # define MIN_NEEDED_OUTPUT MIN_NEEDED_TO | |
497 | # define FROM_LOOP_VX_CU __from_utf16_loop_vx_cu | |
498 | # define LOOPFCT FROM_LOOP_VX_CU | |
499 | # define LOOP_NEED_FLAGS | |
500 | # define BODY BODY_FROM_VX_CU | |
501 | # include <iconv/loop.c> | |
502 | #else | |
503 | # define FROM_LOOP_VX_CU NULL | |
504 | #endif /* HAVE_FROM_VX_CU != 1 */ | |
f957edde AK |
505 | |
506 | /* Conversion from UTF-32 internal/BE to UTF-16. */ | |
507 | ||
85286aaf | 508 | #if HAVE_TO_C == 1 |
f957edde AK |
509 | /* The software routine is copied from utf-16.c (minus bytes |
510 | swapping). */ | |
85286aaf | 511 | # define BODY_TO_C \ |
f957edde | 512 | { \ |
f957edde AK |
513 | uint32_t c = get32 (inptr); \ |
514 | \ | |
515 | if (__builtin_expect (c <= 0xd7ff, 1) \ | |
a42a95c4 | 516 | || (c > 0xdfff && c <= 0xffff)) \ |
f957edde | 517 | { \ |
6896776c SL |
518 | /* Two UTF-16 chars. */ \ |
519 | put16 (outptr, c); \ | |
f957edde AK |
520 | } \ |
521 | else if (__builtin_expect (c >= 0x10000, 1) \ | |
522 | && __builtin_expect (c <= 0x10ffff, 1)) \ | |
523 | { \ | |
524 | /* Four UTF-16 chars. */ \ | |
6896776c | 525 | uint16_t zabcd = ((c & 0x1f0000) >> 16) - 1; \ |
f957edde AK |
526 | uint16_t out; \ |
527 | \ | |
528 | /* Generate a surrogate character. */ \ | |
6896776c | 529 | if (__glibc_unlikely (outptr + 4 > outend)) \ |
f957edde AK |
530 | { \ |
531 | /* Overflow in the output buffer. */ \ | |
532 | result = __GCONV_FULL_OUTPUT; \ | |
533 | break; \ | |
534 | } \ | |
535 | \ | |
536 | out = 0xd800; \ | |
537 | out |= (zabcd & 0xff) << 6; \ | |
538 | out |= (c >> 10) & 0x3f; \ | |
539 | put16 (outptr, out); \ | |
540 | outptr += 2; \ | |
541 | \ | |
542 | out = 0xdc00; \ | |
543 | out |= c & 0x3ff; \ | |
544 | put16 (outptr, out); \ | |
545 | } \ | |
546 | else \ | |
547 | { \ | |
6896776c | 548 | STANDARD_TO_LOOP_ERR_HANDLER (4); \ |
f957edde AK |
549 | } \ |
550 | outptr += 2; \ | |
551 | inptr += 4; \ | |
552 | } | |
6896776c | 553 | |
85286aaf SL |
554 | /* Generate loop-function with software routing. */ |
555 | # define MIN_NEEDED_INPUT MIN_NEEDED_TO | |
556 | # define MIN_NEEDED_OUTPUT MIN_NEEDED_FROM | |
557 | # define MAX_NEEDED_OUTPUT MAX_NEEDED_FROM | |
558 | # define TO_LOOP_C __to_utf16_loop_c | |
559 | # define LOOPFCT TO_LOOP_C | |
560 | # define LOOP_NEED_FLAGS | |
561 | # define BODY BODY_TO_C | |
562 | # include <iconv/loop.c> | |
563 | #else | |
564 | # define TO_LOOP_C NULL | |
565 | #endif /* HAVE_TO_C != 1 */ | |
566 | ||
567 | #if HAVE_TO_VX == 1 | |
568 | # define BODY_TO_VX \ | |
6896776c | 569 | { \ |
a42a95c4 SL |
570 | size_t inlen = inend - inptr; \ |
571 | size_t outlen = outend - outptr; \ | |
6896776c SL |
572 | unsigned long tmp, tmp2, tmp3; \ |
573 | asm volatile (".machine push\n\t" \ | |
574 | ".machine \"z13\"\n\t" \ | |
575 | ".machinemode \"zarch_nohighgprs\"\n\t" \ | |
576 | /* Setup to check for surrogates. */ \ | |
577 | " larl %[R_TMP],9f\n\t" \ | |
578 | " vlm %%v30,%%v31,0(%[R_TMP])\n\t" \ | |
ee518b70 SL |
579 | CONVERT_32BIT_SIZE_T ([R_INLEN]) \ |
580 | CONVERT_32BIT_SIZE_T ([R_OUTLEN]) \ | |
593e4da1 | 581 | /* Loop which handles UTF-32 chars \ |
6896776c | 582 | ch < 0xd800 || (ch > 0xdfff && ch < 0x10000). */ \ |
a42a95c4 SL |
583 | "0: clgijl %[R_INLEN],32,2f\n\t" \ |
584 | " clgijl %[R_OUTLEN],16,2f\n\t" \ | |
6896776c SL |
585 | "1: vlm %%v16,%%v17,0(%[R_IN])\n\t" \ |
586 | " lghi %[R_TMP2],0\n\t" \ | |
587 | /* Shorten to UTF-16. */ \ | |
588 | " vpkf %%v18,%%v16,%%v17\n\t" \ | |
589 | /* Check for surrogate chars. */ \ | |
590 | " vstrcfs %%v19,%%v16,%%v30,%%v31\n\t" \ | |
591 | " jno 10f\n\t" \ | |
592 | " vstrcfs %%v19,%%v17,%%v30,%%v31\n\t" \ | |
593 | " jno 11f\n\t" \ | |
594 | /* Store 16 bytes to buf_out. */ \ | |
595 | " vst %%v18,0(%[R_OUT])\n\t" \ | |
596 | " la %[R_IN],32(%[R_IN])\n\t" \ | |
597 | " aghi %[R_INLEN],-32\n\t" \ | |
598 | " aghi %[R_OUTLEN],-16\n\t" \ | |
599 | " la %[R_OUT],16(%[R_OUT])\n\t" \ | |
a42a95c4 SL |
600 | " clgijl %[R_INLEN],32,2f\n\t" \ |
601 | " clgijl %[R_OUTLEN],16,2f\n\t" \ | |
6896776c | 602 | " j 1b\n\t" \ |
a42a95c4 SL |
603 | /* Calculate remaining uint32_t values in inptr. */ \ |
604 | "2: \n\t" \ | |
605 | " clgije %[R_INLEN],0,99f\n\t" \ | |
606 | " clgijl %[R_INLEN],4,92f\n\t" \ | |
607 | " srlg %[R_TMP2],%[R_INLEN],2\n\t" \ | |
608 | " j 20f\n\t" \ | |
6896776c SL |
609 | /* Setup to check for ch >= 0xd800 && ch <= 0xdfff \ |
610 | and check for ch >= 0x10000. (v30, v31) */ \ | |
611 | "9: .long 0xd800,0xdfff,0x10000,0x10000\n\t" \ | |
612 | " .long 0xa0000000,0xc0000000, 0xa0000000,0xa0000000\n\t" \ | |
613 | /* At least on UTF32 char is in range of surrogates. \ | |
614 | Store the preceding characters. */ \ | |
615 | "11: ahi %[R_TMP2],16\n\t" \ | |
616 | "10: vlgvb %[R_TMP],%%v19,7\n\t" \ | |
617 | " agr %[R_TMP],%[R_TMP2]\n\t" \ | |
618 | " srlg %[R_TMP3],%[R_TMP],1\n\t" /* Number of out bytes. */ \ | |
619 | " ahik %[R_TMP2],%[R_TMP3],-1\n\t" /* Highest index to store. */ \ | |
a42a95c4 | 620 | " jl 12f\n\t" \ |
6896776c SL |
621 | " vstl %%v18,%[R_TMP2],0(%[R_OUT])\n\t" \ |
622 | /* Update pointers. */ \ | |
623 | " la %[R_IN],0(%[R_TMP],%[R_IN])\n\t" \ | |
624 | " slgr %[R_INLEN],%[R_TMP]\n\t" \ | |
625 | " la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t" \ | |
626 | " slgr %[R_OUTLEN],%[R_TMP3]\n\t" \ | |
a42a95c4 SL |
627 | /* Calculate remaining uint32_t values in vrs. */ \ |
628 | "12: lghi %[R_TMP2],8\n\t" \ | |
629 | " srlg %[R_TMP3],%[R_TMP3],1\n\t" \ | |
630 | " slgr %[R_TMP2],%[R_TMP3]\n\t" \ | |
631 | /* Handle remaining UTF-32 characters. */ \ | |
632 | "20: l %[R_TMP],0(%[R_IN])\n\t" \ | |
633 | " aghi %[R_INLEN],-4\n\t" \ | |
634 | /* Test if ch is 2byte UTF-16 char. */ \ | |
635 | " clfi %[R_TMP],0xffff\n\t" \ | |
636 | " jh 21f\n\t" \ | |
637 | /* Handle 2 byte UTF16 char. */ \ | |
638 | " lgr %[R_TMP3],%[R_TMP]\n\t" \ | |
639 | " nilf %[R_TMP],0xf800\n\t" \ | |
640 | " clfi %[R_TMP],0xd800\n\t" \ | |
641 | " je 91f\n\t" /* Do not accept UTF-16 surrogates. */ \ | |
642 | " slgfi %[R_OUTLEN],2\n\t" \ | |
643 | " jl 90f \n\t" \ | |
644 | " sth %[R_TMP3],0(%[R_OUT])\n\t" \ | |
645 | " la %[R_IN],4(%[R_IN])\n\t" \ | |
646 | " la %[R_OUT],2(%[R_OUT])\n\t" \ | |
647 | " brctg %[R_TMP2],20b\n\t" \ | |
648 | " j 0b\n\t" /* Switch to vx-loop. */ \ | |
649 | /* Test if ch is 4byte UTF-16 char. */ \ | |
650 | "21: clfi %[R_TMP],0x10ffff\n\t" \ | |
651 | " jh 91f\n\t" /* ch > 0x10ffff is not allowed! */ \ | |
652 | /* Handle 4 byte UTF16 char. */ \ | |
653 | " slgfi %[R_OUTLEN],4\n\t" \ | |
654 | " jl 90f \n\t" \ | |
655 | " slfi %[R_TMP],0x10000\n\t" /* zabcd = uvwxy - 1. */ \ | |
656 | " llilf %[R_TMP3],0xd800dc00\n\t" \ | |
657 | " la %[R_IN],4(%[R_IN])\n\t" \ | |
658 | " risbgn %[R_TMP3],%[R_TMP],38,47,6\n\t" /* High surrogate. */ \ | |
659 | " risbgn %[R_TMP3],%[R_TMP],54,63,0\n\t" /* Low surrogate. */ \ | |
660 | " st %[R_TMP3],0(%[R_OUT])\n\t" \ | |
661 | " la %[R_OUT],4(%[R_OUT])\n\t" \ | |
662 | " brctg %[R_TMP2],20b\n\t" \ | |
663 | " j 0b\n\t" /* Switch to vx-loop. */ \ | |
664 | "92: lghi %[R_RES],%[RES_IN_FULL]\n\t" \ | |
665 | " j 99f\n\t" \ | |
666 | "91: lghi %[R_RES],%[RES_IN_ILL]\n\t" \ | |
667 | " j 99f\n\t" \ | |
668 | "90: lghi %[R_RES],%[RES_OUT_FULL]\n\t" \ | |
669 | "99: \n\t" \ | |
6896776c | 670 | ".machine pop" \ |
a42a95c4 SL |
671 | : /* outputs */ [R_IN] "+a" (inptr) \ |
672 | , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (outptr) \ | |
6896776c SL |
673 | , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp) \ |
674 | , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3) \ | |
675 | , [R_RES] "+d" (result) \ | |
676 | : /* inputs */ \ | |
677 | [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT) \ | |
678 | , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT) \ | |
679 | , [RES_IN_FULL] "i" (__GCONV_INCOMPLETE_INPUT) \ | |
680 | : /* clobber list */ "memory", "cc" \ | |
681 | ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17") \ | |
682 | ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19") \ | |
683 | ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31") \ | |
684 | ); \ | |
6896776c | 685 | if (__glibc_likely (inptr == inend) \ |
a42a95c4 | 686 | || result != __GCONV_ILLEGAL_INPUT) \ |
6896776c | 687 | break; \ |
a42a95c4 | 688 | \ |
6896776c SL |
689 | STANDARD_TO_LOOP_ERR_HANDLER (4); \ |
690 | } | |
691 | ||
6896776c SL |
692 | /* Generate loop-function with hardware vector instructions. */ |
693 | # define MIN_NEEDED_INPUT MIN_NEEDED_TO | |
694 | # define MIN_NEEDED_OUTPUT MIN_NEEDED_FROM | |
695 | # define MAX_NEEDED_OUTPUT MAX_NEEDED_FROM | |
85286aaf SL |
696 | # define TO_LOOP_VX __to_utf16_loop_vx |
697 | # define LOOPFCT TO_LOOP_VX | |
6896776c SL |
698 | # define LOOP_NEED_FLAGS |
699 | # define BODY BODY_TO_VX | |
700 | # include <iconv/loop.c> | |
85286aaf SL |
701 | #else |
702 | # define TO_LOOP_VX NULL | |
703 | #endif /* HAVE_TO_VX != 1 */ | |
704 | ||
593e4da1 SL |
705 | #if HAVE_TO_VX_CU == 1 |
706 | #define BODY_TO_VX_CU \ | |
707 | { \ | |
708 | register const unsigned char* pInput asm ("8") = inptr; \ | |
709 | register size_t inlen asm ("9") = inend - inptr; \ | |
710 | register unsigned char* pOutput asm ("10") = outptr; \ | |
711 | register size_t outlen asm ("11") = outend - outptr; \ | |
712 | unsigned long tmp, tmp2, tmp3; \ | |
713 | asm volatile (".machine push\n\t" \ | |
714 | ".machine \"z13\"\n\t" \ | |
715 | ".machinemode \"zarch_nohighgprs\"\n\t" \ | |
716 | /* Setup to check for surrogates. */ \ | |
717 | " larl %[R_TMP],9f\n\t" \ | |
718 | " vlm %%v30,%%v31,0(%[R_TMP])\n\t" \ | |
719 | CONVERT_32BIT_SIZE_T ([R_INLEN]) \ | |
720 | CONVERT_32BIT_SIZE_T ([R_OUTLEN]) \ | |
721 | /* Loop which handles UTF-32 chars \ | |
722 | ch < 0xd800 || (ch > 0xdfff && ch < 0x10000). */ \ | |
723 | "0: clgijl %[R_INLEN],32,20f\n\t" \ | |
724 | " clgijl %[R_OUTLEN],16,20f\n\t" \ | |
725 | "1: vlm %%v16,%%v17,0(%[R_IN])\n\t" \ | |
726 | " lghi %[R_TMP2],0\n\t" \ | |
727 | /* Shorten to UTF-16. */ \ | |
728 | " vpkf %%v18,%%v16,%%v17\n\t" \ | |
729 | /* Check for surrogate chars. */ \ | |
730 | " vstrcfs %%v19,%%v16,%%v30,%%v31\n\t" \ | |
731 | " jno 10f\n\t" \ | |
732 | " vstrcfs %%v19,%%v17,%%v30,%%v31\n\t" \ | |
733 | " jno 11f\n\t" \ | |
734 | /* Store 16 bytes to buf_out. */ \ | |
735 | " vst %%v18,0(%[R_OUT])\n\t" \ | |
736 | " la %[R_IN],32(%[R_IN])\n\t" \ | |
737 | " aghi %[R_INLEN],-32\n\t" \ | |
738 | " aghi %[R_OUTLEN],-16\n\t" \ | |
739 | " la %[R_OUT],16(%[R_OUT])\n\t" \ | |
740 | " clgijl %[R_INLEN],32,20f\n\t" \ | |
741 | " clgijl %[R_OUTLEN],16,20f\n\t" \ | |
742 | " j 1b\n\t" \ | |
743 | /* Setup to check for ch >= 0xd800 && ch <= 0xdfff \ | |
744 | and check for ch >= 0x10000. (v30, v31) */ \ | |
745 | "9: .long 0xd800,0xdfff,0x10000,0x10000\n\t" \ | |
746 | " .long 0xa0000000,0xc0000000, 0xa0000000,0xa0000000\n\t" \ | |
747 | /* At least one UTF32 char is in range of surrogates. \ | |
748 | Store the preceding characters. */ \ | |
749 | "11: ahi %[R_TMP2],16\n\t" \ | |
750 | "10: vlgvb %[R_TMP],%%v19,7\n\t" \ | |
751 | " agr %[R_TMP],%[R_TMP2]\n\t" \ | |
752 | " srlg %[R_TMP3],%[R_TMP],1\n\t" /* Number of out bytes. */ \ | |
753 | " ahik %[R_TMP2],%[R_TMP3],-1\n\t" /* Highest index to store. */ \ | |
754 | " jl 20f\n\t" \ | |
755 | " vstl %%v18,%[R_TMP2],0(%[R_OUT])\n\t" \ | |
756 | /* Update pointers. */ \ | |
757 | " la %[R_IN],0(%[R_TMP],%[R_IN])\n\t" \ | |
758 | " slgr %[R_INLEN],%[R_TMP]\n\t" \ | |
759 | " la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t" \ | |
760 | " slgr %[R_OUTLEN],%[R_TMP3]\n\t" \ | |
761 | /* Handles UTF16 surrogates with convert instruction. */ \ | |
762 | "20: cu42 %[R_OUT],%[R_IN]\n\t" \ | |
763 | " jo 0b\n\t" /* Try vector implemenation again. */ \ | |
764 | " lochil %[R_RES],%[RES_OUT_FULL]\n\t" /* cc == 1. */ \ | |
765 | " lochih %[R_RES],%[RES_IN_ILL]\n\t" /* cc == 2. */ \ | |
766 | ".machine pop" \ | |
767 | : /* outputs */ [R_IN] "+a" (pInput) \ | |
768 | , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (pOutput) \ | |
769 | , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp) \ | |
770 | , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3) \ | |
771 | , [R_RES] "+d" (result) \ | |
772 | : /* inputs */ \ | |
773 | [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT) \ | |
774 | , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT) \ | |
775 | : /* clobber list */ "memory", "cc" \ | |
776 | ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17") \ | |
777 | ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19") \ | |
778 | ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31") \ | |
779 | ); \ | |
780 | inptr = pInput; \ | |
781 | outptr = pOutput; \ | |
782 | \ | |
783 | if (__glibc_likely (inlen == 0) \ | |
784 | || result == __GCONV_FULL_OUTPUT) \ | |
785 | break; \ | |
786 | if (inlen < 4) \ | |
787 | { \ | |
788 | result = __GCONV_INCOMPLETE_INPUT; \ | |
789 | break; \ | |
790 | } \ | |
791 | \ | |
792 | STANDARD_TO_LOOP_ERR_HANDLER (4); \ | |
793 | } | |
794 | ||
795 | /* Generate loop-function with hardware vector and utf-convert instructions. */ | |
796 | # define MIN_NEEDED_INPUT MIN_NEEDED_TO | |
797 | # define MIN_NEEDED_OUTPUT MIN_NEEDED_FROM | |
798 | # define MAX_NEEDED_OUTPUT MAX_NEEDED_FROM | |
799 | # define TO_LOOP_VX_CU __to_utf16_loop_vx_cu | |
800 | # define LOOPFCT TO_LOOP_VX_CU | |
801 | # define LOOP_NEED_FLAGS | |
802 | # define BODY BODY_TO_VX_CU | |
803 | # include <iconv/loop.c> | |
804 | #else | |
805 | # define TO_LOOP_VX_CU NULL | |
806 | #endif /* HAVE_TO_VX_CU != 1 */ | |
807 | ||
85286aaf SL |
808 | /* This file also exists in sysdeps/s390/multiarch/ which |
809 | generates ifunc resolvers for FROM/TO_LOOP functions | |
810 | and includes iconv/skeleton.c afterwards. */ | |
811 | #if ! defined USE_MULTIARCH | |
812 | # include <iconv/skeleton.c> | |
6896776c | 813 | #endif |