Blame sysdeps/s390/utf8-utf16-z9.c

Packit 6c4009
/* Conversion between UTF-8 and UTF-16 - s390 version.
Packit 6c4009
Packit 6c4009
   This module uses the Z9-109 variants of the Convert Unicode
Packit 6c4009
   instructions.
Packit 6c4009
   Copyright (C) 1997-2018 Free Software Foundation, Inc.
Packit 6c4009
Packit 6c4009
   Author: Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>
Packit 6c4009
   Based on the work by Ulrich Drepper  <drepper@cygnus.com>, 1997.
Packit 6c4009
Packit 6c4009
   Thanks to Daniel Appich who covered the relevant performance work
Packit 6c4009
   in his diploma thesis.
Packit 6c4009
Packit 6c4009
   This is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <dlfcn.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <gconv.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
Packit 6c4009
/* Select which versions should be defined depending on support
Packit 6c4009
   for multiarch, vector and used minimum architecture level.  */
Packit 6c4009
#ifdef HAVE_S390_MIN_Z196_ZARCH_ASM_SUPPORT
Packit 6c4009
# define HAVE_FROM_C		0
Packit 6c4009
# define FROM_LOOP_DEFAULT	FROM_LOOP_CU
Packit 6c4009
#else
Packit 6c4009
# define HAVE_FROM_C		1
Packit 6c4009
# define FROM_LOOP_DEFAULT	FROM_LOOP_C
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#define HAVE_TO_C		1
Packit 6c4009
#define TO_LOOP_DEFAULT		TO_LOOP_C
Packit 6c4009
Packit 6c4009
#if defined HAVE_S390_MIN_Z196_ZARCH_ASM_SUPPORT || defined USE_MULTIARCH
Packit 6c4009
# define HAVE_FROM_CU		1
Packit 6c4009
#else
Packit 6c4009
# define HAVE_FROM_CU		0
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if defined HAVE_S390_VX_ASM_SUPPORT && defined USE_MULTIARCH
Packit 6c4009
# define HAVE_FROM_VX		1
Packit 6c4009
# define HAVE_TO_VX		1
Packit 6c4009
# define HAVE_TO_VX_CU		1
Packit 6c4009
#else
Packit 6c4009
# define HAVE_FROM_VX		0
Packit 6c4009
# define HAVE_TO_VX		0
Packit 6c4009
# define HAVE_TO_VX_CU		0
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if defined HAVE_S390_VX_GCC_SUPPORT
Packit 6c4009
# define ASM_CLOBBER_VR(NR) , NR
Packit 6c4009
#else
Packit 6c4009
# define ASM_CLOBBER_VR(NR)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if defined __s390x__
Packit 6c4009
# define CONVERT_32BIT_SIZE_T(REG)
Packit 6c4009
#else
Packit 6c4009
# define CONVERT_32BIT_SIZE_T(REG) "llgfr %" #REG ",%" #REG "\n\t"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Defines for skeleton.c.  */
Packit 6c4009
#define DEFINE_INIT		0
Packit 6c4009
#define DEFINE_FINI		0
Packit 6c4009
#define MIN_NEEDED_FROM		1
Packit 6c4009
#define MAX_NEEDED_FROM		4
Packit 6c4009
#define MIN_NEEDED_TO		2
Packit 6c4009
#define MAX_NEEDED_TO		4
Packit 6c4009
#define FROM_LOOP		FROM_LOOP_DEFAULT
Packit 6c4009
#define TO_LOOP			TO_LOOP_DEFAULT
Packit 6c4009
#define FROM_DIRECTION		(dir == from_utf8)
Packit 6c4009
#define ONE_DIRECTION           0
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* UTF-16 big endian byte order mark.  */
Packit 6c4009
#define BOM_UTF16	0xfeff
Packit 6c4009
Packit 6c4009
/* Direction of the transformation.  */
Packit 6c4009
enum direction
Packit 6c4009
{
Packit 6c4009
  illegal_dir,
Packit 6c4009
  to_utf8,
Packit 6c4009
  from_utf8
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
struct utf8_data
Packit 6c4009
{
Packit 6c4009
  enum direction dir;
Packit 6c4009
  int emit_bom;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
extern int gconv_init (struct __gconv_step *step);
Packit 6c4009
int
Packit 6c4009
gconv_init (struct __gconv_step *step)
Packit 6c4009
{
Packit 6c4009
  /* Determine which direction.  */
Packit 6c4009
  struct utf8_data *new_data;
Packit 6c4009
  enum direction dir = illegal_dir;
Packit 6c4009
  int emit_bom;
Packit 6c4009
  int result;
Packit 6c4009
Packit 6c4009
  emit_bom = (__strcasecmp (step->__to_name, "UTF-16//") == 0);
Packit 6c4009
Packit 6c4009
  if (__strcasecmp (step->__from_name, "ISO-10646/UTF8/") == 0
Packit 6c4009
      && (__strcasecmp (step->__to_name, "UTF-16//") == 0
Packit 6c4009
	  || __strcasecmp (step->__to_name, "UTF-16BE//") == 0))
Packit 6c4009
    {
Packit 6c4009
      dir = from_utf8;
Packit 6c4009
    }
Packit 6c4009
  else if (__strcasecmp (step->__from_name, "UTF-16BE//") == 0
Packit 6c4009
	   && __strcasecmp (step->__to_name, "ISO-10646/UTF8/") == 0)
Packit 6c4009
    {
Packit 6c4009
      dir = to_utf8;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  result = __GCONV_NOCONV;
Packit 6c4009
  if (dir != illegal_dir)
Packit 6c4009
    {
Packit 6c4009
      new_data = (struct utf8_data *) malloc (sizeof (struct utf8_data));
Packit 6c4009
Packit 6c4009
      result = __GCONV_NOMEM;
Packit 6c4009
      if (new_data != NULL)
Packit 6c4009
	{
Packit 6c4009
	  new_data->dir = dir;
Packit 6c4009
	  new_data->emit_bom = emit_bom;
Packit 6c4009
	  step->__data = new_data;
Packit 6c4009
Packit 6c4009
	  if (dir == from_utf8)
Packit 6c4009
	    {
Packit 6c4009
	      step->__min_needed_from = MIN_NEEDED_FROM;
Packit 6c4009
	      step->__max_needed_from = MIN_NEEDED_FROM;
Packit 6c4009
	      step->__min_needed_to = MIN_NEEDED_TO;
Packit 6c4009
	      step->__max_needed_to = MIN_NEEDED_TO;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      step->__min_needed_from = MIN_NEEDED_TO;
Packit 6c4009
	      step->__max_needed_from = MIN_NEEDED_TO;
Packit 6c4009
	      step->__min_needed_to = MIN_NEEDED_FROM;
Packit 6c4009
	      step->__max_needed_to = MIN_NEEDED_FROM;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  step->__stateful = 0;
Packit 6c4009
Packit 6c4009
	  result = __GCONV_OK;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
extern void gconv_end (struct __gconv_step *data);
Packit 6c4009
void
Packit 6c4009
gconv_end (struct __gconv_step *data)
Packit 6c4009
{
Packit 6c4009
  free (data->__data);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* The macro for the hardware loop.  This is used for both
Packit 6c4009
   directions.  */
Packit 6c4009
#define HARDWARE_CONVERT(INSTRUCTION)					\
Packit 6c4009
  {									\
Packit 6c4009
    register const unsigned char* pInput __asm__ ("8") = inptr;		\
Packit 6c4009
    register size_t inlen __asm__ ("9") = inend - inptr;		\
Packit 6c4009
    register unsigned char* pOutput __asm__ ("10") = outptr;		\
Packit 6c4009
    register size_t outlen __asm__("11") = outend - outptr;		\
Packit 6c4009
    unsigned long cc = 0;						\
Packit 6c4009
									\
Packit 6c4009
    __asm__ __volatile__ (".machine push       \n\t"			\
Packit 6c4009
			  ".machine \"z9-109\" \n\t"			\
Packit 6c4009
			  ".machinemode \"zarch_nohighgprs\"\n\t"	\
Packit 6c4009
			  "0: " INSTRUCTION "  \n\t"			\
Packit 6c4009
			  ".machine pop        \n\t"			\
Packit 6c4009
			  "   jo     0b        \n\t"			\
Packit 6c4009
			  "   ipm    %2        \n"			\
Packit 6c4009
			  : "+a" (pOutput), "+a" (pInput), "+d" (cc),	\
Packit 6c4009
			    "+d" (outlen), "+d" (inlen)			\
Packit 6c4009
			  :						\
Packit 6c4009
			  : "cc", "memory");				\
Packit 6c4009
									\
Packit 6c4009
    inptr = pInput;							\
Packit 6c4009
    outptr = pOutput;							\
Packit 6c4009
    cc >>= 28;								\
Packit 6c4009
									\
Packit 6c4009
    if (cc == 1)							\
Packit 6c4009
      {									\
Packit 6c4009
	result = __GCONV_FULL_OUTPUT;					\
Packit 6c4009
      }									\
Packit 6c4009
    else if (cc == 2)							\
Packit 6c4009
      {									\
Packit 6c4009
	result = __GCONV_ILLEGAL_INPUT;					\
Packit 6c4009
      }									\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
#define PREPARE_LOOP							\
Packit 6c4009
  enum direction dir = ((struct utf8_data *) step->__data)->dir;	\
Packit 6c4009
  int emit_bom = ((struct utf8_data *) step->__data)->emit_bom;		\
Packit 6c4009
									\
Packit 6c4009
  if (emit_bom && !data->__internal_use					\
Packit 6c4009
      && data->__invocation_counter == 0)				\
Packit 6c4009
    {									\
Packit 6c4009
      /* Emit the UTF-16 Byte Order Mark.  */				\
Packit 6c4009
      if (__glibc_unlikely (outbuf + 2 > outend))			\
Packit 6c4009
	return __GCONV_FULL_OUTPUT;					\
Packit 6c4009
									\
Packit 6c4009
      put16u (outbuf, BOM_UTF16);					\
Packit 6c4009
      outbuf += 2;							\
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
/* Conversion function from UTF-8 to UTF-16.  */
Packit 6c4009
#define BODY_FROM_HW(ASM)						\
Packit 6c4009
  {									\
Packit 6c4009
    ASM;								\
Packit 6c4009
    if (__glibc_likely (inptr == inend)					\
Packit 6c4009
	|| result == __GCONV_FULL_OUTPUT)				\
Packit 6c4009
      break;								\
Packit 6c4009
									\
Packit 6c4009
    int i;								\
Packit 6c4009
    for (i = 1; inptr + i < inend && i < 5; ++i)			\
Packit 6c4009
      if ((inptr[i] & 0xc0) != 0x80)					\
Packit 6c4009
	break;								\
Packit 6c4009
									\
Packit 6c4009
    if (__glibc_likely (inptr + i == inend				\
Packit 6c4009
			&& result == __GCONV_EMPTY_INPUT))		\
Packit 6c4009
      {									\
Packit 6c4009
	result = __GCONV_INCOMPLETE_INPUT;				\
Packit 6c4009
	break;								\
Packit 6c4009
      }									\
Packit 6c4009
    STANDARD_FROM_LOOP_ERR_HANDLER (i);					\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
#if HAVE_FROM_VX == 1
Packit 6c4009
# define HW_FROM_VX							\
Packit 6c4009
  {									\
Packit 6c4009
    register const unsigned char* pInput asm ("8") = inptr;		\
Packit 6c4009
    register size_t inlen asm ("9") = inend - inptr;			\
Packit 6c4009
    register unsigned char* pOutput asm ("10") = outptr;		\
Packit 6c4009
    register size_t outlen asm("11") = outend - outptr;			\
Packit 6c4009
    unsigned long tmp, tmp2, tmp3;					\
Packit 6c4009
    asm volatile (".machine push\n\t"					\
Packit 6c4009
		  ".machine \"z13\"\n\t"				\
Packit 6c4009
		  ".machinemode \"zarch_nohighgprs\"\n\t"		\
Packit 6c4009
		  "    vrepib %%v30,0x7f\n\t" /* For compare > 0x7f.  */ \
Packit 6c4009
		  "    vrepib %%v31,0x20\n\t"				\
Packit 6c4009
		  CONVERT_32BIT_SIZE_T ([R_INLEN])			\
Packit 6c4009
		  CONVERT_32BIT_SIZE_T ([R_OUTLEN])			\
Packit 6c4009
		  /* Loop which handles UTF-8 chars <=0x7f.  */		\
Packit 6c4009
		  "0:  clgijl %[R_INLEN],16,20f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],32,20f\n\t"			\
Packit 6c4009
		  "1:  vl %%v16,0(%[R_IN])\n\t"				\
Packit 6c4009
		  "    vstrcbs %%v17,%%v16,%%v30,%%v31\n\t"		\
Packit 6c4009
		  "    jno 10f\n\t" /* Jump away if not all bytes are 1byte \
Packit 6c4009
				       UTF8 chars.  */			\
Packit 6c4009
		  /* Enlarge to UTF-16.  */				\
Packit 6c4009
		  "    vuplhb %%v18,%%v16\n\t"				\
Packit 6c4009
		  "    la %[R_IN],16(%[R_IN])\n\t"			\
Packit 6c4009
		  "    vupllb %%v19,%%v16\n\t"				\
Packit 6c4009
		  "    aghi %[R_INLEN],-16\n\t"				\
Packit 6c4009
		  /* Store 32 bytes to buf_out.  */			\
Packit 6c4009
		  "    vstm %%v18,%%v19,0(%[R_OUT])\n\t"		\
Packit 6c4009
		  "    aghi %[R_OUTLEN],-32\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],32(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    clgijl %[R_INLEN],16,20f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],32,20f\n\t"			\
Packit 6c4009
		  "    j 1b\n\t"					\
Packit 6c4009
		  "10:\n\t"						\
Packit 6c4009
		  /* At least one byte is > 0x7f.			\
Packit 6c4009
		     Store the preceding 1-byte chars.  */		\
Packit 6c4009
		  "    vlgvb %[R_TMP],%%v17,7\n\t"			\
Packit 6c4009
		  "    sllk %[R_TMP2],%[R_TMP],1\n\t" /* Compute highest \
Packit 6c4009
							 index to store. */ \
Packit 6c4009
		  "    llgfr %[R_TMP3],%[R_TMP2]\n\t"			\
Packit 6c4009
		  "    ahi %[R_TMP2],-1\n\t"				\
Packit 6c4009
		  "    jl 20f\n\t"					\
Packit 6c4009
		  "    vuplhb %%v18,%%v16\n\t"				\
Packit 6c4009
		  "    vstl %%v18,%[R_TMP2],0(%[R_OUT])\n\t"		\
Packit 6c4009
		  "    ahi %[R_TMP2],-16\n\t"				\
Packit 6c4009
		  "    jl 11f\n\t"					\
Packit 6c4009
		  "    vupllb %%v19,%%v16\n\t"				\
Packit 6c4009
		  "    vstl %%v19,%[R_TMP2],16(%[R_OUT])\n\t"		\
Packit 6c4009
		  "11: \n\t" /* Update pointers.  */			\
Packit 6c4009
		  "    la %[R_IN],0(%[R_TMP],%[R_IN])\n\t"		\
Packit 6c4009
		  "    slgr %[R_INLEN],%[R_TMP]\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t"		\
Packit 6c4009
		  "    slgr %[R_OUTLEN],%[R_TMP3]\n\t"			\
Packit 6c4009
		  /* Handle multibyte utf8-char with convert instruction. */ \
Packit 6c4009
		  "20: cu12 %[R_OUT],%[R_IN],1\n\t"			\
Packit 6c4009
		  "    jo 0b\n\t" /* Try vector implemenation again.  */ \
Packit 6c4009
		  "    lochil %[R_RES],%[RES_OUT_FULL]\n\t" /* cc == 1.  */ \
Packit 6c4009
		  "    lochih %[R_RES],%[RES_IN_ILL]\n\t" /* cc == 2.  */ \
Packit 6c4009
		  ".machine pop"					\
Packit 6c4009
		  : /* outputs */ [R_IN] "+a" (pInput)			\
Packit 6c4009
		    , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (pOutput)	\
Packit 6c4009
		    , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp)	\
Packit 6c4009
		    , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3)	\
Packit 6c4009
		    , [R_RES] "+d" (result)				\
Packit 6c4009
		  : /* inputs */					\
Packit 6c4009
		    [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT)		\
Packit 6c4009
		    , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT)		\
Packit 6c4009
		  : /* clobber list */ "memory", "cc"			\
Packit 6c4009
		    ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31")	\
Packit 6c4009
		  );							\
Packit 6c4009
    inptr = pInput;							\
Packit 6c4009
    outptr = pOutput;							\
Packit 6c4009
  }
Packit 6c4009
# define BODY_FROM_VX BODY_FROM_HW (HW_FROM_VX)
Packit 6c4009
Packit 6c4009
/* Generate loop-function with hardware vector and utf-convert instructions.  */
Packit 6c4009
# define MIN_NEEDED_INPUT	MIN_NEEDED_FROM
Packit 6c4009
# define MAX_NEEDED_INPUT	MAX_NEEDED_FROM
Packit 6c4009
# define MIN_NEEDED_OUTPUT	MIN_NEEDED_TO
Packit 6c4009
# define MAX_NEEDED_OUTPUT	MAX_NEEDED_TO
Packit 6c4009
# define FROM_LOOP_VX		__from_utf8_loop_vx
Packit 6c4009
# define LOOPFCT		FROM_LOOP_VX
Packit 6c4009
# define LOOP_NEED_FLAGS
Packit 6c4009
# define BODY			BODY_FROM_VX
Packit 6c4009
# include <iconv/loop.c>
Packit 6c4009
#else
Packit 6c4009
# define FROM_LOOP_VX		NULL
Packit 6c4009
#endif /* HAVE_FROM_VX != 1  */
Packit 6c4009
Packit 6c4009
#if HAVE_FROM_CU == 1
Packit 6c4009
# define BODY_FROM_ETF3EH BODY_FROM_HW (HARDWARE_CONVERT ("cu12 %0, %1, 1"))
Packit 6c4009
Packit 6c4009
/* Generate loop-function with hardware utf-convert instruction.  */
Packit 6c4009
# define MIN_NEEDED_INPUT	MIN_NEEDED_FROM
Packit 6c4009
# define MAX_NEEDED_INPUT	MAX_NEEDED_FROM
Packit 6c4009
# define MIN_NEEDED_OUTPUT	MIN_NEEDED_TO
Packit 6c4009
# define MAX_NEEDED_OUTPUT	MAX_NEEDED_TO
Packit 6c4009
# define FROM_LOOP_CU		__from_utf8_loop_etf3eh
Packit 6c4009
# define LOOPFCT		FROM_LOOP_CU
Packit 6c4009
# define LOOP_NEED_FLAGS
Packit 6c4009
# define BODY			BODY_FROM_ETF3EH
Packit 6c4009
# include <iconv/loop.c>
Packit 6c4009
#else
Packit 6c4009
# define FROM_LOOP_CU		NULL
Packit 6c4009
#endif /* HAVE_FROM_CU != 1  */
Packit 6c4009
Packit 6c4009
#if HAVE_FROM_C == 1
Packit 6c4009
/* The software implementation is based on the code in gconv_simple.c.  */
Packit 6c4009
# define BODY_FROM_C							\
Packit 6c4009
  {									\
Packit 6c4009
    /* Next input byte.  */						\
Packit 6c4009
    uint16_t ch = *inptr;						\
Packit 6c4009
									\
Packit 6c4009
    if (__glibc_likely (ch < 0x80))					\
Packit 6c4009
      {									\
Packit 6c4009
	/* One byte sequence.  */					\
Packit 6c4009
	++inptr;							\
Packit 6c4009
      }									\
Packit 6c4009
    else								\
Packit 6c4009
      {									\
Packit 6c4009
	uint_fast32_t cnt;						\
Packit 6c4009
	uint_fast32_t i;						\
Packit 6c4009
									\
Packit 6c4009
	if (ch >= 0xc2 && ch < 0xe0)					\
Packit 6c4009
	  {								\
Packit 6c4009
	    /* We expect two bytes.  The first byte cannot be 0xc0	\
Packit 6c4009
	       or 0xc1, otherwise the wide character could have been	\
Packit 6c4009
	       represented using a single byte.  */			\
Packit 6c4009
	    cnt = 2;							\
Packit 6c4009
	    ch &= 0x1f;							\
Packit 6c4009
	  }								\
Packit 6c4009
	else if (__glibc_likely ((ch & 0xf0) == 0xe0))			\
Packit 6c4009
	  {								\
Packit 6c4009
	    /* We expect three bytes.  */				\
Packit 6c4009
	    cnt = 3;							\
Packit 6c4009
	    ch &= 0x0f;							\
Packit 6c4009
	  }								\
Packit 6c4009
	else if (__glibc_likely ((ch & 0xf8) == 0xf0))			\
Packit 6c4009
	  {								\
Packit 6c4009
	    /* We expect four bytes.  */				\
Packit 6c4009
	    cnt = 4;							\
Packit 6c4009
	    ch &= 0x07;							\
Packit 6c4009
	  }								\
Packit 6c4009
	else								\
Packit 6c4009
	  {								\
Packit 6c4009
	    /* Search the end of this ill-formed UTF-8 character.  This	\
Packit 6c4009
	       is the next byte with (x & 0xc0) != 0x80.  */		\
Packit 6c4009
	    i = 0;							\
Packit 6c4009
	    do								\
Packit 6c4009
	      ++i;							\
Packit 6c4009
	    while (inptr + i < inend					\
Packit 6c4009
		   && (*(inptr + i) & 0xc0) == 0x80			\
Packit 6c4009
		   && i < 5);						\
Packit 6c4009
									\
Packit 6c4009
	  errout:							\
Packit 6c4009
	    STANDARD_FROM_LOOP_ERR_HANDLER (i);				\
Packit 6c4009
	  }								\
Packit 6c4009
									\
Packit 6c4009
	if (__glibc_unlikely (inptr + cnt > inend))			\
Packit 6c4009
	  {								\
Packit 6c4009
	    /* We don't have enough input.  But before we report	\
Packit 6c4009
	       that check that all the bytes are correct.  */		\
Packit 6c4009
	    for (i = 1; inptr + i < inend; ++i)				\
Packit 6c4009
	      if ((inptr[i] & 0xc0) != 0x80)				\
Packit 6c4009
		break;							\
Packit 6c4009
									\
Packit 6c4009
	    if (__glibc_likely (inptr + i == inend))			\
Packit 6c4009
	      {								\
Packit 6c4009
		result = __GCONV_INCOMPLETE_INPUT;			\
Packit 6c4009
		break;							\
Packit 6c4009
	      }								\
Packit 6c4009
									\
Packit 6c4009
	    goto errout;						\
Packit 6c4009
	  }								\
Packit 6c4009
									\
Packit 6c4009
	if (cnt == 4)							\
Packit 6c4009
	  {								\
Packit 6c4009
	    /* For 4 byte UTF-8 chars two UTF-16 chars (high and	\
Packit 6c4009
	       low) are needed.  */					\
Packit 6c4009
	    uint16_t zabcd, high, low;					\
Packit 6c4009
									\
Packit 6c4009
	    if (__glibc_unlikely (outptr + 4 > outend))			\
Packit 6c4009
	      {								\
Packit 6c4009
		/* Overflow in the output buffer.  */			\
Packit 6c4009
		result = __GCONV_FULL_OUTPUT;				\
Packit 6c4009
		break;							\
Packit 6c4009
	      }								\
Packit 6c4009
									\
Packit 6c4009
	    /* Check if tail-bytes >= 0x80, < 0xc0.  */			\
Packit 6c4009
	    for (i = 1; i < cnt; ++i)					\
Packit 6c4009
	      {								\
Packit 6c4009
		if ((inptr[i] & 0xc0) != 0x80)				\
Packit 6c4009
		  /* This is an illegal encoding.  */			\
Packit 6c4009
		  goto errout;						\
Packit 6c4009
	      }								\
Packit 6c4009
									\
Packit 6c4009
	    /* See Principles of Operations cu12.  */			\
Packit 6c4009
	    zabcd = (((inptr[0] & 0x7) << 2) |				\
Packit 6c4009
		     ((inptr[1] & 0x30) >> 4)) - 1;			\
Packit 6c4009
									\
Packit 6c4009
	    /* z-bit must be zero after subtracting 1.  */		\
Packit 6c4009
	    if (zabcd & 0x10)						\
Packit 6c4009
	      STANDARD_FROM_LOOP_ERR_HANDLER (4)			\
Packit 6c4009
									\
Packit 6c4009
	    high = (uint16_t)(0xd8 << 8);       /* high surrogate id */ \
Packit 6c4009
	    high |= zabcd << 6;                         /* abcd bits */	\
Packit 6c4009
	    high |= (inptr[1] & 0xf) << 2;              /* efgh bits */	\
Packit 6c4009
	    high |= (inptr[2] & 0x30) >> 4;               /* ij bits */	\
Packit 6c4009
									\
Packit 6c4009
	    low = (uint16_t)(0xdc << 8);         /* low surrogate id */ \
Packit 6c4009
	    low |= ((uint16_t)inptr[2] & 0xc) << 6;       /* kl bits */	\
Packit 6c4009
	    low |= (inptr[2] & 0x3) << 6;                 /* mn bits */	\
Packit 6c4009
	    low |= inptr[3] & 0x3f;                   /* opqrst bits */	\
Packit 6c4009
									\
Packit 6c4009
	    put16 (outptr, high);					\
Packit 6c4009
	    outptr += 2;						\
Packit 6c4009
	    put16 (outptr, low);					\
Packit 6c4009
	    outptr += 2;						\
Packit 6c4009
	    inptr += 4;							\
Packit 6c4009
	    continue;							\
Packit 6c4009
	  }								\
Packit 6c4009
	else								\
Packit 6c4009
	  {								\
Packit 6c4009
	    /* Read the possible remaining bytes.  */			\
Packit 6c4009
	    for (i = 1; i < cnt; ++i)					\
Packit 6c4009
	      {								\
Packit 6c4009
		uint16_t byte = inptr[i];				\
Packit 6c4009
									\
Packit 6c4009
		if ((byte & 0xc0) != 0x80)				\
Packit 6c4009
		  /* This is an illegal encoding.  */			\
Packit 6c4009
		  break;						\
Packit 6c4009
									\
Packit 6c4009
		ch <<= 6;						\
Packit 6c4009
		ch |= byte & 0x3f;					\
Packit 6c4009
	      }								\
Packit 6c4009
									\
Packit 6c4009
	    /* If i < cnt, some trail byte was not >= 0x80, < 0xc0.	\
Packit 6c4009
	       If cnt > 2 and ch < 2^(5*cnt-4), the wide character ch could \
Packit 6c4009
	       have been represented with fewer than cnt bytes.  */	\
Packit 6c4009
	    if (i < cnt || (cnt > 2 && (ch >> (5 * cnt - 4)) == 0)	\
Packit 6c4009
		/* Do not accept UTF-16 surrogates.  */			\
Packit 6c4009
		|| (ch >= 0xd800 && ch <= 0xdfff))			\
Packit 6c4009
	      {								\
Packit 6c4009
		/* This is an illegal encoding.  */			\
Packit 6c4009
		goto errout;						\
Packit 6c4009
	      }								\
Packit 6c4009
									\
Packit 6c4009
	    inptr += cnt;						\
Packit 6c4009
	  }								\
Packit 6c4009
      }									\
Packit 6c4009
    /* Now adjust the pointers and store the result.  */		\
Packit 6c4009
    *((uint16_t *) outptr) = ch;					\
Packit 6c4009
    outptr += sizeof (uint16_t);					\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
/* Generate loop-function with software implementation.  */
Packit 6c4009
# define MIN_NEEDED_INPUT	MIN_NEEDED_FROM
Packit 6c4009
# define MAX_NEEDED_INPUT	MAX_NEEDED_FROM
Packit 6c4009
# define MIN_NEEDED_OUTPUT	MIN_NEEDED_TO
Packit 6c4009
# define MAX_NEEDED_OUTPUT	MAX_NEEDED_TO
Packit 6c4009
# define FROM_LOOP_C		__from_utf8_loop_c
Packit 6c4009
# define LOOPFCT		FROM_LOOP_C
Packit 6c4009
# define LOOP_NEED_FLAGS
Packit 6c4009
# define BODY			BODY_FROM_C
Packit 6c4009
# include <iconv/loop.c>
Packit 6c4009
#else
Packit 6c4009
# define FROM_LOOP_C		NULL
Packit 6c4009
#endif /* HAVE_FROM_C != 1  */
Packit 6c4009
Packit 6c4009
/* Conversion from UTF-16 to UTF-8.  */
Packit 6c4009
Packit 6c4009
#if HAVE_TO_C == 1
Packit 6c4009
/* The software routine is based on the functionality of the S/390
Packit 6c4009
   hardware instruction (cu21) as described in the Principles of
Packit 6c4009
   Operation.  */
Packit 6c4009
# define BODY_TO_C							\
Packit 6c4009
  {									\
Packit 6c4009
    uint16_t c = get16 (inptr);						\
Packit 6c4009
									\
Packit 6c4009
    if (__glibc_likely (c <= 0x007f))					\
Packit 6c4009
      {									\
Packit 6c4009
	/* Single byte UTF-8 char.  */					\
Packit 6c4009
	*outptr = c & 0xff;						\
Packit 6c4009
	outptr++;							\
Packit 6c4009
      }									\
Packit 6c4009
    else if (c >= 0x0080 && c <= 0x07ff)				\
Packit 6c4009
      {									\
Packit 6c4009
	/* Two byte UTF-8 char.  */					\
Packit 6c4009
									\
Packit 6c4009
	if (__glibc_unlikely (outptr + 2 > outend))			\
Packit 6c4009
	  {								\
Packit 6c4009
	    /* Overflow in the output buffer.  */			\
Packit 6c4009
	    result = __GCONV_FULL_OUTPUT;				\
Packit 6c4009
	    break;							\
Packit 6c4009
	  }								\
Packit 6c4009
									\
Packit 6c4009
	outptr[0] = 0xc0;						\
Packit 6c4009
	outptr[0] |= c >> 6;						\
Packit 6c4009
									\
Packit 6c4009
	outptr[1] = 0x80;						\
Packit 6c4009
	outptr[1] |= c & 0x3f;						\
Packit 6c4009
									\
Packit 6c4009
	outptr += 2;							\
Packit 6c4009
      }									\
Packit 6c4009
    else if ((c >= 0x0800 && c <= 0xd7ff) || c > 0xdfff)		\
Packit 6c4009
      {									\
Packit 6c4009
	/* Three byte UTF-8 char.  */					\
Packit 6c4009
									\
Packit 6c4009
	if (__glibc_unlikely (outptr + 3 > outend))			\
Packit 6c4009
	  {								\
Packit 6c4009
	    /* Overflow in the output buffer.  */			\
Packit 6c4009
	    result = __GCONV_FULL_OUTPUT;				\
Packit 6c4009
	    break;							\
Packit 6c4009
	  }								\
Packit 6c4009
	outptr[0] = 0xe0;						\
Packit 6c4009
	outptr[0] |= c >> 12;						\
Packit 6c4009
									\
Packit 6c4009
	outptr[1] = 0x80;						\
Packit 6c4009
	outptr[1] |= (c >> 6) & 0x3f;					\
Packit 6c4009
									\
Packit 6c4009
	outptr[2] = 0x80;						\
Packit 6c4009
	outptr[2] |= c & 0x3f;						\
Packit 6c4009
									\
Packit 6c4009
	outptr += 3;							\
Packit 6c4009
      }									\
Packit 6c4009
    else if (c >= 0xd800 && c <= 0xdbff)				\
Packit 6c4009
      {									\
Packit 6c4009
	/* Four byte UTF-8 char.  */					\
Packit 6c4009
	uint16_t low, uvwxy;						\
Packit 6c4009
									\
Packit 6c4009
	if (__glibc_unlikely (outptr + 4 > outend))			\
Packit 6c4009
	  {								\
Packit 6c4009
	    /* Overflow in the output buffer.  */			\
Packit 6c4009
	    result = __GCONV_FULL_OUTPUT;				\
Packit 6c4009
	    break;							\
Packit 6c4009
	  }								\
Packit 6c4009
	if (__glibc_unlikely (inptr + 4 > inend))			\
Packit 6c4009
	  {								\
Packit 6c4009
	    result = __GCONV_INCOMPLETE_INPUT;				\
Packit 6c4009
	    break;							\
Packit 6c4009
	  }								\
Packit 6c4009
									\
Packit 6c4009
	inptr += 2;							\
Packit 6c4009
	low = get16 (inptr);						\
Packit 6c4009
									\
Packit 6c4009
	if ((low & 0xfc00) != 0xdc00)					\
Packit 6c4009
	  {								\
Packit 6c4009
	    inptr -= 2;							\
Packit 6c4009
	    STANDARD_TO_LOOP_ERR_HANDLER (2);				\
Packit 6c4009
	  }								\
Packit 6c4009
	uvwxy = ((c >> 6) & 0xf) + 1;					\
Packit 6c4009
	outptr[0] = 0xf0;						\
Packit 6c4009
	outptr[0] |= uvwxy >> 2;					\
Packit 6c4009
									\
Packit 6c4009
	outptr[1] = 0x80;						\
Packit 6c4009
	outptr[1] |= (uvwxy << 4) & 0x30;				\
Packit 6c4009
	outptr[1] |= (c >> 2) & 0x0f;					\
Packit 6c4009
									\
Packit 6c4009
	outptr[2] = 0x80;						\
Packit 6c4009
	outptr[2] |= (c & 0x03) << 4;					\
Packit 6c4009
	outptr[2] |= (low >> 6) & 0x0f;					\
Packit 6c4009
									\
Packit 6c4009
	outptr[3] = 0x80;						\
Packit 6c4009
	outptr[3] |= low & 0x3f;					\
Packit 6c4009
									\
Packit 6c4009
	outptr += 4;							\
Packit 6c4009
      }									\
Packit 6c4009
    else								\
Packit 6c4009
      {									\
Packit 6c4009
	STANDARD_TO_LOOP_ERR_HANDLER (2);				\
Packit 6c4009
      }									\
Packit 6c4009
    inptr += 2;								\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
/* Generate loop-function with software implementation.  */
Packit 6c4009
# define MIN_NEEDED_INPUT	MIN_NEEDED_TO
Packit 6c4009
# define MAX_NEEDED_INPUT	MAX_NEEDED_TO
Packit 6c4009
# define MIN_NEEDED_OUTPUT	MIN_NEEDED_FROM
Packit 6c4009
# define MAX_NEEDED_OUTPUT	MAX_NEEDED_FROM
Packit 6c4009
# define TO_LOOP_C		__to_utf8_loop_c
Packit 6c4009
# define LOOPFCT		TO_LOOP_C
Packit 6c4009
# define BODY                   BODY_TO_C
Packit 6c4009
# define LOOP_NEED_FLAGS
Packit 6c4009
# include <iconv/loop.c>
Packit 6c4009
#else
Packit 6c4009
# define TO_LOOP_C		NULL
Packit 6c4009
#endif /* HAVE_TO_C != 1  */
Packit 6c4009
Packit 6c4009
#if HAVE_TO_VX == 1
Packit 6c4009
# define BODY_TO_VX							\
Packit 6c4009
  {									\
Packit 6c4009
    size_t inlen  = inend - inptr;					\
Packit 6c4009
    size_t outlen  = outend - outptr;					\
Packit 6c4009
    unsigned long tmp, tmp2, tmp3;					\
Packit 6c4009
    asm volatile (".machine push\n\t"					\
Packit 6c4009
		  ".machine \"z13\"\n\t"				\
Packit 6c4009
		  ".machinemode \"zarch_nohighgprs\"\n\t"		\
Packit 6c4009
		  /* Setup to check for values <= 0x7f.  */		\
Packit 6c4009
		  "    larl %[R_TMP],9f\n\t"				\
Packit 6c4009
		  "    vlm %%v30,%%v31,0(%[R_TMP])\n\t"			\
Packit 6c4009
		  CONVERT_32BIT_SIZE_T ([R_INLEN])			\
Packit 6c4009
		  CONVERT_32BIT_SIZE_T ([R_OUTLEN])			\
Packit 6c4009
		  /* Loop which handles UTF-16 chars <=0x7f.  */	\
Packit 6c4009
		  "0:  clgijl %[R_INLEN],32,2f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],16,2f\n\t"			\
Packit 6c4009
		  "1:  vlm %%v16,%%v17,0(%[R_IN])\n\t"			\
Packit 6c4009
		  "    lghi %[R_TMP2],0\n\t"				\
Packit 6c4009
		  /* Check for > 1byte UTF-8 chars.  */			\
Packit 6c4009
		  "    vstrchs %%v19,%%v16,%%v30,%%v31\n\t"		\
Packit 6c4009
		  "    jno 10f\n\t" /* Jump away if not all bytes are 1byte \
Packit 6c4009
				       UTF8 chars.  */			\
Packit 6c4009
		  "    vstrchs %%v19,%%v17,%%v30,%%v31\n\t"		\
Packit 6c4009
		  "    jno 11f\n\t" /* Jump away if not all bytes are 1byte \
Packit 6c4009
				       UTF8 chars.  */			\
Packit 6c4009
		  /* Shorten to UTF-8.  */				\
Packit 6c4009
		  "    vpkh %%v18,%%v16,%%v17\n\t"			\
Packit 6c4009
		  "    la %[R_IN],32(%[R_IN])\n\t"			\
Packit 6c4009
		  "    aghi %[R_INLEN],-32\n\t"				\
Packit 6c4009
		  /* Store 16 bytes to buf_out.  */			\
Packit 6c4009
		  "    vst %%v18,0(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    aghi %[R_OUTLEN],-16\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],16(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    clgijl %[R_INLEN],32,2f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],16,2f\n\t"			\
Packit 6c4009
		  "    j 1b\n\t"					\
Packit 6c4009
		  /* Setup to check for ch > 0x7f. (v30, v31)  */	\
Packit 6c4009
		  "9:  .short 0x7f,0x7f,0x0,0x0,0x0,0x0,0x0,0x0\n\t"	\
Packit 6c4009
		  "    .short 0x2000,0x2000,0x0,0x0,0x0,0x0,0x0,0x0\n\t" \
Packit 6c4009
		  /* At least one byte is > 0x7f.			\
Packit 6c4009
		     Store the preceding 1-byte chars.  */		\
Packit 6c4009
		  "11: lghi %[R_TMP2],16\n\t" /* match was found in v17.  */ \
Packit 6c4009
		  "10:\n\t"						\
Packit 6c4009
		  "    vlgvb %[R_TMP],%%v19,7\n\t"			\
Packit 6c4009
		  /* Shorten to UTF-8.  */				\
Packit 6c4009
		  "    vpkh %%v18,%%v16,%%v17\n\t"			\
Packit 6c4009
		  "    ar %[R_TMP],%[R_TMP2]\n\t" /* Number of in bytes.  */ \
Packit 6c4009
		  "    srlg %[R_TMP3],%[R_TMP],1\n\t" /* Number of out bytes.  */ \
Packit 6c4009
		  "    ahik %[R_TMP2],%[R_TMP3],-1\n\t" /* Highest index to store.  */ \
Packit 6c4009
		  "    jl 13f\n\t"					\
Packit 6c4009
		  "    vstl %%v18,%[R_TMP2],0(%[R_OUT])\n\t"		\
Packit 6c4009
		  /* Update pointers.  */				\
Packit 6c4009
		  "    la %[R_IN],0(%[R_TMP],%[R_IN])\n\t"		\
Packit 6c4009
		  "    slgr %[R_INLEN],%[R_TMP]\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t"		\
Packit 6c4009
		  "    slgr %[R_OUTLEN],%[R_TMP3]\n\t"			\
Packit 6c4009
		  "13: \n\t"						\
Packit 6c4009
		  /* Calculate remaining uint16_t values in loaded vrs.  */ \
Packit 6c4009
		  "    lghi %[R_TMP2],16\n\t"				\
Packit 6c4009
		  "    slgr %[R_TMP2],%[R_TMP3]\n\t"			\
Packit 6c4009
		  "    llh %[R_TMP],0(%[R_IN])\n\t"			\
Packit 6c4009
		  "    aghi %[R_INLEN],-2\n\t"				\
Packit 6c4009
		  "    j 22f\n\t"					\
Packit 6c4009
		  /* Handle remaining bytes.  */			\
Packit 6c4009
		  "2:  \n\t"						\
Packit 6c4009
		  /* Zero, one or more bytes available?  */		\
Packit 6c4009
		  "    clgfi %[R_INLEN],1\n\t"				\
Packit 6c4009
		  "    locghie %[R_RES],%[RES_IN_FULL]\n\t" /* Only one byte.  */ \
Packit 6c4009
		  "    jle 99f\n\t" /* End if less than two bytes.  */	\
Packit 6c4009
		  /* Calculate remaining uint16_t values in inptr.  */	\
Packit 6c4009
		  "    srlg %[R_TMP2],%[R_INLEN],1\n\t"			\
Packit 6c4009
		  /* Handle multibyte utf8-char. */			\
Packit 6c4009
		  "20: llh %[R_TMP],0(%[R_IN])\n\t"			\
Packit 6c4009
		  "    aghi %[R_INLEN],-2\n\t"				\
Packit 6c4009
		  /* Test if ch is 1-byte UTF-8 char.  */		\
Packit 6c4009
		  "21: clijh %[R_TMP],0x7f,22f\n\t"			\
Packit 6c4009
		  /* Handle 1-byte UTF-8 char.  */			\
Packit 6c4009
		  "31: slgfi %[R_OUTLEN],1\n\t"				\
Packit 6c4009
		  "    jl 90f \n\t"					\
Packit 6c4009
		  "    stc %[R_TMP],0(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    la %[R_IN],2(%[R_IN])\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],1(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    brctg %[R_TMP2],20b\n\t"				\
Packit 6c4009
		  "    j 0b\n\t" /* Switch to vx-loop.  */		\
Packit 6c4009
		  /* Test if ch is 2-byte UTF-8 char.  */		\
Packit 6c4009
		  "22: clfi %[R_TMP],0x7ff\n\t"				\
Packit 6c4009
		  "    jh 23f\n\t"					\
Packit 6c4009
		  /* Handle 2-byte UTF-8 char.  */			\
Packit 6c4009
		  "32: slgfi %[R_OUTLEN],2\n\t"				\
Packit 6c4009
		  "    jl 90f \n\t"					\
Packit 6c4009
		  "    llill %[R_TMP3],0xc080\n\t"			\
Packit 6c4009
		  "    la %[R_IN],2(%[R_IN])\n\t"			\
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],51,55,2\n\t" /* 1. byte.   */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 2. byte.   */ \
Packit 6c4009
		  "    sth %[R_TMP3],0(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],2(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    brctg %[R_TMP2],20b\n\t"				\
Packit 6c4009
		  "    j 0b\n\t" /* Switch to vx-loop.  */		\
Packit 6c4009
		  /* Test if ch is 3-byte UTF-8 char.  */		\
Packit 6c4009
		  "23: clfi %[R_TMP],0xd7ff\n\t"			\
Packit 6c4009
		  "    jh 24f\n\t"					\
Packit 6c4009
		  /* Handle 3-byte UTF-8 char.  */			\
Packit 6c4009
		  "33: slgfi %[R_OUTLEN],3\n\t"				\
Packit 6c4009
		  "    jl 90f \n\t"					\
Packit 6c4009
		  "    llilf %[R_TMP3],0xe08080\n\t"			\
Packit 6c4009
		  "    la %[R_IN],2(%[R_IN])\n\t"			\
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],44,47,4\n\t" /* 1. byte.  */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],50,55,2\n\t" /* 2. byte.  */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 3. byte.  */ \
Packit 6c4009
		  "    stcm %[R_TMP3],7,0(%[R_OUT])\n\t"		\
Packit 6c4009
		  "    la %[R_OUT],3(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    brctg %[R_TMP2],20b\n\t"				\
Packit 6c4009
		  "    j 0b\n\t" /* Switch to vx-loop.  */		\
Packit 6c4009
		  /* Test if ch is 4-byte UTF-8 char.  */		\
Packit 6c4009
		  "24: clfi %[R_TMP],0xdfff\n\t"			\
Packit 6c4009
		  "    jh 33b\n\t" /* Handle this 3-byte UTF-8 char.  */ \
Packit 6c4009
		  "    clfi %[R_TMP],0xdbff\n\t"			\
Packit 6c4009
		  "    locghih %[R_RES],%[RES_IN_ILL]\n\t"		\
Packit 6c4009
		  "    jh 99f\n\t" /* Jump away if this is a low surrogate \
Packit 6c4009
				      without a preceding high surrogate.  */ \
Packit 6c4009
		  /* Handle 4-byte UTF-8 char.  */			\
Packit 6c4009
		  "34: slgfi %[R_OUTLEN],4\n\t"				\
Packit 6c4009
		  "    jl 90f \n\t"					\
Packit 6c4009
		  "    slgfi %[R_INLEN],2\n\t"				\
Packit 6c4009
		  "    locghil %[R_RES],%[RES_IN_FULL]\n\t"		\
Packit 6c4009
		  "    jl 99f\n\t" /* Jump away if low surrogate is missing.  */ \
Packit 6c4009
		  "    llilf %[R_TMP3],0xf0808080\n\t"			\
Packit 6c4009
		  "    aghi %[R_TMP],0x40\n\t"				\
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],37,39,16\n\t" /* 1. byte: uvw  */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],42,43,14\n\t" /* 2. byte: xy  */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],44,47,14\n\t" /* 2. byte: efgh  */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],50,51,12\n\t" /* 3. byte: ij */ \
Packit 6c4009
		  "    llh %[R_TMP],2(%[R_IN])\n\t" /* Load low surrogate.  */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],52,55,2\n\t" /* 3. byte: klmn  */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 4. byte: opqrst  */ \
Packit 6c4009
		  "    nilf %[R_TMP],0xfc00\n\t"			\
Packit 6c4009
		  "    clfi %[R_TMP],0xdc00\n\t" /* Check if it starts with 0xdc00.  */ \
Packit 6c4009
		  "    locghine %[R_RES],%[RES_IN_ILL]\n\t"		\
Packit 6c4009
		  "    jne 99f\n\t" /* Jump away if low surrogate is invalid.  */ \
Packit 6c4009
		  "    st %[R_TMP3],0(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    la %[R_IN],4(%[R_IN])\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],4(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    aghi %[R_TMP2],-2\n\t"				\
Packit 6c4009
		  "    jh 20b\n\t"					\
Packit 6c4009
		  "    j 0b\n\t" /* Switch to vx-loop.  */		\
Packit 6c4009
		  /* Exit with __GCONV_FULL_OUTPUT.  */			\
Packit 6c4009
		  "90: lghi %[R_RES],%[RES_OUT_FULL]\n\t"		\
Packit 6c4009
		  "99: \n\t"						\
Packit 6c4009
		  ".machine pop"					\
Packit 6c4009
		  : /* outputs */ [R_IN] "+a" (inptr)			\
Packit 6c4009
		    , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (outptr)	\
Packit 6c4009
		    , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp)	\
Packit 6c4009
		    , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3)	\
Packit 6c4009
		    , [R_RES] "+d" (result)				\
Packit 6c4009
		  : /* inputs */					\
Packit 6c4009
		    [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT)		\
Packit 6c4009
		    , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT)		\
Packit 6c4009
		    , [RES_IN_FULL] "i" (__GCONV_INCOMPLETE_INPUT)	\
Packit 6c4009
		  : /* clobber list */ "memory", "cc"			\
Packit 6c4009
		    ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31")	\
Packit 6c4009
		  );							\
Packit 6c4009
    if (__glibc_likely (inptr == inend)					\
Packit 6c4009
	|| result != __GCONV_ILLEGAL_INPUT)				\
Packit 6c4009
      break;								\
Packit 6c4009
									\
Packit 6c4009
    STANDARD_TO_LOOP_ERR_HANDLER (2);					\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
/* Generate loop-function with vector implementation.  */
Packit 6c4009
# define MIN_NEEDED_INPUT	MIN_NEEDED_TO
Packit 6c4009
# define MAX_NEEDED_INPUT	MAX_NEEDED_TO
Packit 6c4009
# define MIN_NEEDED_OUTPUT	MIN_NEEDED_FROM
Packit 6c4009
# define MAX_NEEDED_OUTPUT	MAX_NEEDED_FROM
Packit 6c4009
# define TO_LOOP_VX		__to_utf8_loop_vx
Packit 6c4009
# define LOOPFCT		TO_LOOP_VX
Packit 6c4009
# define BODY                   BODY_TO_VX
Packit 6c4009
# define LOOP_NEED_FLAGS
Packit 6c4009
# include <iconv/loop.c>
Packit 6c4009
#else
Packit 6c4009
# define TO_LOOP_VX		NULL
Packit 6c4009
#endif /* HAVE_TO_VX != 1  */
Packit 6c4009
Packit 6c4009
#if HAVE_TO_VX_CU == 1
Packit 6c4009
#define BODY_TO_VX_CU							\
Packit 6c4009
  {									\
Packit 6c4009
    register const unsigned char* pInput asm ("8") = inptr;		\
Packit 6c4009
    register size_t inlen asm ("9") = inend - inptr;			\
Packit 6c4009
    register unsigned char* pOutput asm ("10") = outptr;		\
Packit 6c4009
    register size_t outlen asm ("11") = outend - outptr;		\
Packit 6c4009
    unsigned long tmp, tmp2, tmp3;					\
Packit 6c4009
    asm volatile (".machine push\n\t"					\
Packit 6c4009
		  ".machine \"z13\"\n\t"				\
Packit 6c4009
		  ".machinemode \"zarch_nohighgprs\"\n\t"		\
Packit 6c4009
		  /* Setup to check for values <= 0x7f.  */		\
Packit 6c4009
		  "    larl %[R_TMP],9f\n\t"				\
Packit 6c4009
		  "    vlm %%v30,%%v31,0(%[R_TMP])\n\t"			\
Packit 6c4009
		  CONVERT_32BIT_SIZE_T ([R_INLEN])			\
Packit 6c4009
		  CONVERT_32BIT_SIZE_T ([R_OUTLEN])			\
Packit 6c4009
		  /* Loop which handles UTF-16 chars <=0x7f.  */	\
Packit 6c4009
		  "0:  clgijl %[R_INLEN],32,20f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],16,20f\n\t"			\
Packit 6c4009
		  "1:  vlm %%v16,%%v17,0(%[R_IN])\n\t"			\
Packit 6c4009
		  "    lghi %[R_TMP2],0\n\t"				\
Packit 6c4009
		  /* Check for > 1byte UTF-8 chars.  */			\
Packit 6c4009
		  "    vstrchs %%v19,%%v16,%%v30,%%v31\n\t"		\
Packit 6c4009
		  "    jno 10f\n\t" /* Jump away if not all bytes are 1byte \
Packit 6c4009
				       UTF8 chars.  */			\
Packit 6c4009
		  "    vstrchs %%v19,%%v17,%%v30,%%v31\n\t"		\
Packit 6c4009
		  "    jno 11f\n\t" /* Jump away if not all bytes are 1byte \
Packit 6c4009
				       UTF8 chars.  */			\
Packit 6c4009
		  /* Shorten to UTF-8.  */				\
Packit 6c4009
		  "    vpkh %%v18,%%v16,%%v17\n\t"			\
Packit 6c4009
		  "    la %[R_IN],32(%[R_IN])\n\t"			\
Packit 6c4009
		  "    aghi %[R_INLEN],-32\n\t"				\
Packit 6c4009
		  /* Store 16 bytes to buf_out.  */			\
Packit 6c4009
		  "    vst %%v18,0(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    aghi %[R_OUTLEN],-16\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],16(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    clgijl %[R_INLEN],32,20f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],16,20f\n\t"			\
Packit 6c4009
		  "    j 1b\n\t"					\
Packit 6c4009
		  /* Setup to check for ch > 0x7f. (v30, v31)  */	\
Packit 6c4009
		  "9:  .short 0x7f,0x7f,0x0,0x0,0x0,0x0,0x0,0x0\n\t"	\
Packit 6c4009
		  "    .short 0x2000,0x2000,0x0,0x0,0x0,0x0,0x0,0x0\n\t" \
Packit 6c4009
		  /* At least one byte is > 0x7f.			\
Packit 6c4009
		     Store the preceding 1-byte chars.  */		\
Packit 6c4009
		  "11: lghi %[R_TMP2],16\n\t" /* match was found in v17.  */ \
Packit 6c4009
		  "10: vlgvb %[R_TMP],%%v19,7\n\t"			\
Packit 6c4009
		  /* Shorten to UTF-8.  */				\
Packit 6c4009
		  "    vpkh %%v18,%%v16,%%v17\n\t"			\
Packit 6c4009
		  "    ar %[R_TMP],%[R_TMP2]\n\t" /* Number of in bytes.  */ \
Packit 6c4009
		  "    srlg %[R_TMP3],%[R_TMP],1\n\t" /* Number of out bytes.  */ \
Packit 6c4009
		  "    ahik %[R_TMP2],%[R_TMP3],-1\n\t" /* Highest index to store.  */ \
Packit 6c4009
		  "    jl 20f\n\t"					\
Packit 6c4009
		  "    vstl %%v18,%[R_TMP2],0(%[R_OUT])\n\t"		\
Packit 6c4009
		  /* Update pointers.  */				\
Packit 6c4009
		  "    la %[R_IN],0(%[R_TMP],%[R_IN])\n\t"		\
Packit 6c4009
		  "    slgr %[R_INLEN],%[R_TMP]\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],0(%[R_TMP3],%[R_OUT])\n\t"		\
Packit 6c4009
		  "    slgr %[R_OUTLEN],%[R_TMP3]\n\t"			\
Packit 6c4009
		  /* Handles UTF16 surrogates with convert instruction.  */ \
Packit 6c4009
		  "20: cu21 %[R_OUT],%[R_IN],1\n\t"			\
Packit 6c4009
		  "    jo 0b\n\t" /* Try vector implemenation again.  */ \
Packit 6c4009
		  "    lochil %[R_RES],%[RES_OUT_FULL]\n\t" /* cc == 1.  */ \
Packit 6c4009
		  "    lochih %[R_RES],%[RES_IN_ILL]\n\t" /* cc == 2.  */ \
Packit 6c4009
		  ".machine pop"					\
Packit 6c4009
		  : /* outputs */ [R_IN] "+a" (pInput)			\
Packit 6c4009
		    , [R_INLEN] "+d" (inlen), [R_OUT] "+a" (pOutput)	\
Packit 6c4009
		    , [R_OUTLEN] "+d" (outlen), [R_TMP] "=a" (tmp)	\
Packit 6c4009
		    , [R_TMP2] "=d" (tmp2), [R_TMP3] "=a" (tmp3)	\
Packit 6c4009
		    , [R_RES] "+d" (result)				\
Packit 6c4009
		  : /* inputs */					\
Packit 6c4009
		    [RES_OUT_FULL] "i" (__GCONV_FULL_OUTPUT)		\
Packit 6c4009
		    , [RES_IN_ILL] "i" (__GCONV_ILLEGAL_INPUT)		\
Packit 6c4009
		  : /* clobber list */ "memory", "cc"			\
Packit 6c4009
		    ASM_CLOBBER_VR ("v16") ASM_CLOBBER_VR ("v17")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v18") ASM_CLOBBER_VR ("v19")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v30") ASM_CLOBBER_VR ("v31")	\
Packit 6c4009
		  );							\
Packit 6c4009
    inptr = pInput;							\
Packit 6c4009
    outptr = pOutput;							\
Packit 6c4009
									\
Packit 6c4009
    if (__glibc_likely (inlen == 0)					\
Packit 6c4009
	|| result == __GCONV_FULL_OUTPUT)				\
Packit 6c4009
      break;								\
Packit 6c4009
    if (inlen == 1)							\
Packit 6c4009
      {									\
Packit 6c4009
	/* Input does not contain a complete utf16 character.  */	\
Packit 6c4009
	result = __GCONV_INCOMPLETE_INPUT;				\
Packit 6c4009
	break;								\
Packit 6c4009
      }									\
Packit 6c4009
    else if (result != __GCONV_ILLEGAL_INPUT)				\
Packit 6c4009
      {									\
Packit 6c4009
	/* Input is >= 2 and < 4 bytes (as cu21 would have processed	\
Packit 6c4009
	   a possible next utf16 character) and not illegal.		\
Packit 6c4009
	   => we have a single high surrogate at end of input.  */	\
Packit 6c4009
	result = __GCONV_INCOMPLETE_INPUT;				\
Packit 6c4009
	break;								\
Packit 6c4009
      }									\
Packit 6c4009
									\
Packit 6c4009
    STANDARD_TO_LOOP_ERR_HANDLER (2);					\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
/* Generate loop-function with vector and utf-convert instructions.  */
Packit 6c4009
# define MIN_NEEDED_INPUT	MIN_NEEDED_TO
Packit 6c4009
# define MAX_NEEDED_INPUT	MAX_NEEDED_TO
Packit 6c4009
# define MIN_NEEDED_OUTPUT	MIN_NEEDED_FROM
Packit 6c4009
# define MAX_NEEDED_OUTPUT	MAX_NEEDED_FROM
Packit 6c4009
# define TO_LOOP_VX_CU		__to_utf8_loop_vx_cu
Packit 6c4009
# define LOOPFCT		TO_LOOP_VX_CU
Packit 6c4009
# define BODY                   BODY_TO_VX_CU
Packit 6c4009
# define LOOP_NEED_FLAGS
Packit 6c4009
# include <iconv/loop.c>
Packit 6c4009
#else
Packit 6c4009
# define TO_LOOP_VX_CU		NULL
Packit 6c4009
#endif /* HAVE_TO_VX_CU != 1  */
Packit 6c4009
Packit 6c4009
/* This file also exists in sysdeps/s390/multiarch/ which
Packit 6c4009
   generates ifunc resolvers for FROM/TO_LOOP functions
Packit 6c4009
   and includes iconv/skeleton.c afterwards.  */
Packit 6c4009
#if ! defined USE_MULTIARCH
Packit 6c4009
# include <iconv/skeleton.c>
Packit 6c4009
#endif