(root)/
glibc-2.38/
sysdeps/
s390/
utf16-utf32-z9.c
       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.
       5     Copyright (C) 1997-2023 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  #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
      36  # define HAVE_FROM_VX_CU	1
      37  # define HAVE_TO_VX		1
      38  # define HAVE_TO_VX_CU		1
      39  #else
      40  # define HAVE_FROM_VX		0
      41  # define HAVE_FROM_VX_CU	0
      42  # define HAVE_TO_VX		0
      43  # define HAVE_TO_VX_CU		0
      44  #endif
      45  
      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  
      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  
      58  /* UTF-32 big endian byte order mark.  */
      59  #define BOM_UTF32               0x0000feffu
      60  
      61  /* UTF-16 big endian byte order mark.  */
      62  #define BOM_UTF16               0xfeff
      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
      69  #define FROM_LOOP		FROM_LOOP_DEFAULT
      70  #define TO_LOOP			TO_LOOP_DEFAULT
      71  #define FROM_DIRECTION		(dir == from_utf16)
      72  #define ONE_DIRECTION           0
      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
     105  	  || __strcasecmp (step->__to_name, "INTERNAL") == 0))
     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  
     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  	  put16 (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  	  put32 (outbuf, BOM_UTF32);					\
     184  	  outbuf += 4;							\
     185  	}								\
     186      }
     187  
     188  /* Conversion function from UTF-16 to UTF-32 internal/BE.  */
     189  
     190  #if HAVE_FROM_C == 1
     191  /* The software routine is copied from utf-16.c (minus bytes
     192     swapping).  */
     193  # define BODY_FROM_C							\
     194    {									\
     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        {									\
     205  	/* An isolated low-surrogate was found.  This has to be         \
     206  	   considered ill-formed.  */					\
     207  	if (__glibc_unlikely (u1 >= 0xdc00))				\
     208  	  {								\
     209  	    STANDARD_FROM_LOOP_ERR_HANDLER (2);				\
     210  	  }								\
     211  	/* It's a surrogate character.  At least the first word says	\
     212  	   it is.  */							\
     213  	if (__glibc_unlikely (inptr + 4 > inend))			\
     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    }
     236  
     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							\
     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"			\
     263  		  CONVERT_32BIT_SIZE_T ([R_INLEN])			\
     264  		  CONVERT_32BIT_SIZE_T ([R_OUTLEN])			\
     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" \
     287  		  /* At least one uint16_t is in range of surrogates.	\
     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"				\
     306  		  "    slgr %[R_TMP2],%[R_TMP]\n\t"			\
     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  
     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
     386  # define FROM_LOOP_VX		__from_utf16_loop_vx
     387  # define LOOPFCT		FROM_LOOP_VX
     388  # define LOOP_NEED_FLAGS
     389  # define BODY			BODY_FROM_VX
     390  # include <iconv/loop.c>
     391  #else
     392  # define FROM_LOOP_VX		NULL
     393  #endif /* HAVE_FROM_VX != 1  */
     394  
     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 implementation 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  */
     505  
     506  /* Conversion from UTF-32 internal/BE to UTF-16.  */
     507  
     508  #if HAVE_TO_C == 1
     509  /* The software routine is copied from utf-16.c (minus bytes
     510     swapping).  */
     511  # define BODY_TO_C							\
     512    {									\
     513      uint32_t c = get32 (inptr);						\
     514  									\
     515      if (__builtin_expect (c <= 0xd7ff, 1)				\
     516  	|| (c > 0xdfff && c <= 0xffff))					\
     517        {									\
     518  	/* Two UTF-16 chars.  */					\
     519  	put16 (outptr, c);						\
     520        }									\
     521      else if (__builtin_expect (c >= 0x10000, 1)				\
     522  	     && __builtin_expect (c <= 0x10ffff, 1))			\
     523        {									\
     524  	/* Four UTF-16 chars.  */					\
     525  	uint16_t zabcd = ((c & 0x1f0000) >> 16) - 1;			\
     526  	uint16_t out;							\
     527  									\
     528  	/* Generate a surrogate character.  */				\
     529  	if (__glibc_unlikely (outptr + 4 > outend))			\
     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        {									\
     548  	STANDARD_TO_LOOP_ERR_HANDLER (4);				\
     549        }									\
     550      outptr += 2;							\
     551      inptr += 4;								\
     552    }
     553  
     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							\
     569    {									\
     570      size_t inlen = inend - inptr;					\
     571      size_t outlen = outend - outptr;					\
     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"			\
     579  		  CONVERT_32BIT_SIZE_T ([R_INLEN])			\
     580  		  CONVERT_32BIT_SIZE_T ([R_OUTLEN])			\
     581  		  /* Loop which handles UTF-32 chars			\
     582  		     ch < 0xd800 || (ch > 0xdfff && ch < 0x10000).  */	\
     583  		  "0:  clgijl %[R_INLEN],32,2f\n\t"			\
     584  		  "    clgijl %[R_OUTLEN],16,2f\n\t"			\
     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"			\
     600  		  "    clgijl %[R_INLEN],32,2f\n\t"			\
     601  		  "    clgijl %[R_OUTLEN],16,2f\n\t"			\
     602  		  "    j 1b\n\t"					\
     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"					\
     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.  */ \
     620  		  "    jl 12f\n\t"					\
     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"			\
     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"						\
     670  		  ".machine pop"					\
     671  		  : /* outputs */ [R_IN] "+a" (inptr)			\
     672  		    , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (outptr)	\
     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  		  );							\
     685      if (__glibc_likely (inptr == inend)					\
     686  	|| result != __GCONV_ILLEGAL_INPUT)				\
     687        break;								\
     688  									\
     689      STANDARD_TO_LOOP_ERR_HANDLER (4);					\
     690    }
     691  
     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
     696  # define TO_LOOP_VX		__to_utf16_loop_vx
     697  # define LOOPFCT		TO_LOOP_VX
     698  # define LOOP_NEED_FLAGS
     699  # define BODY			BODY_TO_VX
     700  # include <iconv/loop.c>
     701  #else
     702  # define TO_LOOP_VX		NULL
     703  #endif /* HAVE_TO_VX != 1  */
     704  
     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 implementation 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  
     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>
     813  #endif