]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/s390/utf16-utf32-z9.c
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / sysdeps / s390 / utf16-utf32-z9.c
CommitLineData
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. */
75enum direction
76{
77 illegal_dir,
78 to_utf16,
79 from_utf16
80};
81
82struct utf16_data
83{
84 enum direction dir;
85 int emit_bom;
86};
87
88
89extern int gconv_init (struct __gconv_step *step);
90int
91gconv_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
154extern void gconv_end (struct __gconv_step *data);
155void
156gconv_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