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
gconv_init(struct __gconv_step * step)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 (step->__to_name, "UTF-32//") == 0);
108 
109   if (__strcasecmp (step->__from_name, "ISO-10646/UTF8/") == 0
110       && (__strcasecmp (step->__to_name, "UTF-32//") == 0
111 	  || __strcasecmp (step->__to_name, "UTF-32BE//") == 0
112 	  || __strcasecmp (step->__to_name, "INTERNAL") == 0))
113     {
114       dir = from_utf8;
115     }
116   else if (__strcasecmp (step->__to_name, "ISO-10646/UTF8/") == 0
117 	   && (__strcasecmp (step->__from_name, "UTF-32BE//") == 0
118 	       || __strcasecmp (step->__from_name, "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 (sizeof (struct utf8_data));
127 
128       result = __GCONV_NOMEM;
129       if (new_data != NULL)
130 	{
131 	  new_data->dir = dir;
132 	  new_data->emit_bom = emit_bom;
133 	  step->__data = new_data;
134 
135 	  if (dir == from_utf8)
136 	    {
137 	      step->__min_needed_from = MIN_NEEDED_FROM;
138 	      step->__max_needed_from = MIN_NEEDED_FROM;
139 	      step->__min_needed_to = MIN_NEEDED_TO;
140 	      step->__max_needed_to = MIN_NEEDED_TO;
141 	    }
142 	  else
143 	    {
144 	      step->__min_needed_from = MIN_NEEDED_TO;
145 	      step->__max_needed_from = MIN_NEEDED_TO;
146 	      step->__min_needed_to = MIN_NEEDED_FROM;
147 	      step->__max_needed_to = MIN_NEEDED_FROM;
148 	    }
149 
150 	  step->__stateful = 0;
151 
152 	  result = __GCONV_OK;
153 	}
154     }
155 
156   return result;
157 }
158 
159 
160 extern void gconv_end (struct __gconv_step *data);
161 void
gconv_end(struct __gconv_step * data)162 gconv_end (struct __gconv_step *data)
163 {
164   free (data->__data);
165 }
166 
167 /* The macro for the hardware loop.  This is used for both
168    directions.  */
169 #define HARDWARE_CONVERT(INSTRUCTION)					\
170   {									\
171     register const unsigned char* pInput __asm__ ("8") = inptr;		\
172     register size_t inlen __asm__ ("9") = inend - inptr;		\
173     register unsigned char* pOutput __asm__ ("10") = outptr;		\
174     register size_t outlen __asm__("11") = outend - outptr;		\
175     unsigned long cc = 0;						\
176 									\
177     __asm__ __volatile__ (".machine push       \n\t"			\
178 			  ".machine \"z9-109\" \n\t"			\
179 			  ".machinemode \"zarch_nohighgprs\"\n\t"	\
180 			  "0: " INSTRUCTION "  \n\t"			\
181 			  ".machine pop        \n\t"			\
182 			  "   jo     0b        \n\t"			\
183 			  "   ipm    %2        \n"			\
184 			  : "+a" (pOutput), "+a" (pInput), "+d" (cc),	\
185 			    "+d" (outlen), "+d" (inlen)			\
186 			  :						\
187 			  : "cc", "memory");				\
188 									\
189     inptr = pInput;							\
190     outptr = pOutput;							\
191     cc >>= 28;								\
192 									\
193     if (cc == 1)							\
194       {									\
195 	result = __GCONV_FULL_OUTPUT;					\
196       }									\
197     else if (cc == 2)							\
198       {									\
199 	result = __GCONV_ILLEGAL_INPUT;					\
200       }									\
201   }
202 
203 #define PREPARE_LOOP							\
204   enum direction dir = ((struct utf8_data *) step->__data)->dir;	\
205   int emit_bom = ((struct utf8_data *) step->__data)->emit_bom;		\
206 									\
207   if (emit_bom && !data->__internal_use					\
208       && data->__invocation_counter == 0)				\
209     {									\
210       /* Emit the 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