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

Packit 6c4009
/* Conversion between UTF-8 and UTF-32 BE/internal.
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		6
Packit 6c4009
#define MIN_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
/* UTF-32 big endian byte order mark.  */
Packit 6c4009
#define BOM			0x0000feffu
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-32//") == 0);
Packit 6c4009
Packit 6c4009
  if (__strcasecmp (step->__from_name, "ISO-10646/UTF8/") == 0
Packit 6c4009
      && (__strcasecmp (step->__to_name, "UTF-32//") == 0
Packit 6c4009
	  || __strcasecmp (step->__to_name, "UTF-32BE//") == 0
Packit 6c4009
	  || __strcasecmp (step->__to_name, "INTERNAL") == 0))
Packit 6c4009
    {
Packit 6c4009
      dir = from_utf8;
Packit 6c4009
    }
Packit 6c4009
  else if (__strcasecmp (step->__to_name, "ISO-10646/UTF8/") == 0
Packit 6c4009
	   && (__strcasecmp (step->__from_name, "UTF-32BE//") == 0
Packit 6c4009
	       || __strcasecmp (step->__from_name, "INTERNAL") == 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 Byte Order Mark.  */					\
Packit 6c4009
      if (__glibc_unlikely (outbuf + 4 > outend))			\
Packit 6c4009
	return __GCONV_FULL_OUTPUT;					\
Packit 6c4009
									\
Packit 6c4009
      put32u (outbuf, BOM);						\
Packit 6c4009
      outbuf += 4;							\
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
/* Conversion function from UTF-8 to UTF-32 internal/BE.  */
Packit 6c4009
Packit 6c4009
#define STORE_REST_COMMON						      \
Packit 6c4009
  {									      \
Packit 6c4009
    /* We store the remaining bytes while converting them into the UCS4	      \
Packit 6c4009
       format.  We can assume that the first byte in the buffer is	      \
Packit 6c4009
       correct and that it requires a larger number of bytes than there	      \
Packit 6c4009
       are in the input buffer.  */					      \
Packit 6c4009
    wint_t ch = **inptrp;						      \
Packit 6c4009
    size_t cnt, r;							      \
Packit 6c4009
									      \
Packit 6c4009
    state->__count = inend - *inptrp;					      \
Packit 6c4009
									      \
Packit 6c4009
    assert (ch != 0xc0 && ch != 0xc1);					      \
Packit 6c4009
    if (ch >= 0xc2 && ch < 0xe0)					      \
Packit 6c4009
      {									      \
Packit 6c4009
	/* We expect two bytes.  The first byte cannot be 0xc0 or	      \
Packit 6c4009
	   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 if (__glibc_likely ((ch & 0xfc) == 0xf8))			      \
Packit 6c4009
      {									      \
Packit 6c4009
	/* We expect five bytes.  */					      \
Packit 6c4009
	cnt = 5;							      \
Packit 6c4009
	ch &= 0x03;							      \
Packit 6c4009
      }									      \
Packit 6c4009
    else								      \
Packit 6c4009
      {									      \
Packit 6c4009
	/* We expect six bytes.  */					      \
Packit 6c4009
	cnt = 6;							      \
Packit 6c4009
	ch &= 0x01;							      \
Packit 6c4009
      }									      \
Packit 6c4009
									      \
Packit 6c4009
    /* The first byte is already consumed.  */				      \
Packit 6c4009
    r = cnt - 1;							      \
Packit 6c4009
    while (++(*inptrp) < inend)						      \
Packit 6c4009
      {									      \
Packit 6c4009
	ch <<= 6;							      \
Packit 6c4009
	ch |= **inptrp & 0x3f;						      \
Packit 6c4009
	--r;								      \
Packit 6c4009
      }									      \
Packit 6c4009
									      \
Packit 6c4009
    /* Shift for the so far missing bytes.  */				      \
Packit 6c4009
    ch <<= r * 6;							      \
Packit 6c4009
									      \
Packit 6c4009
    /* Store the number of bytes expected for the entire sequence.  */	      \
Packit 6c4009
    state->__count |= cnt << 8;						      \
Packit 6c4009
									      \
Packit 6c4009
    /* Store the value.  */						      \
Packit 6c4009
    state->__value.__wch = ch;						      \
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
#define UNPACK_BYTES_COMMON \
Packit 6c4009
  {									      \
Packit 6c4009
    static const unsigned char inmask[5] = { 0xc0, 0xe0, 0xf0, 0xf8, 0xfc };  \
Packit 6c4009
    wint_t wch = state->__value.__wch;					      \
Packit 6c4009
    size_t ntotal = state->__count >> 8;				      \
Packit 6c4009
									      \
Packit 6c4009
    inlen = state->__count & 255;					      \
Packit 6c4009
									      \
Packit 6c4009
    bytebuf[0] = inmask[ntotal - 2];					      \
Packit 6c4009
									      \
Packit 6c4009
    do									      \
Packit 6c4009
      {									      \
Packit 6c4009
	if (--ntotal < inlen)						      \
Packit 6c4009
	  bytebuf[ntotal] = 0x80 | (wch & 0x3f);			      \
Packit 6c4009
	wch >>= 6;							      \
Packit 6c4009
      }									      \
Packit 6c4009
    while (ntotal > 1);							      \
Packit 6c4009
									      \
Packit 6c4009
    bytebuf[0] |= wch;							      \
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
#define CLEAR_STATE_COMMON \
Packit 6c4009
  state->__count = 0
Packit 6c4009
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_C == 1
Packit 6c4009
/* The software routine is copied from gconv_simple.c.  */
Packit 6c4009
# define BODY_FROM_C							\
Packit 6c4009
  {									\
Packit 6c4009
    /* Next input byte.  */						\
Packit 6c4009
    uint32_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 or	\
Packit 6c4009
	       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
	/* Read the possible remaining bytes.  */			\
Packit 6c4009
	for (i = 1; i < cnt; ++i)					\
Packit 6c4009
	  {								\
Packit 6c4009
	    uint32_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
	    || (ch > 0x10ffff))						\
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
    *((uint32_t *) outptr) = ch;					\
Packit 6c4009
    outptr += sizeof (uint32_t);					\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
/* These definitions apply to the UTF-8 to UTF-32 direction.  The
Packit 6c4009
   software implementation for UTF-8 still supports multibyte
Packit 6c4009
   characters up to 6 bytes whereas the hardware variant does not.  */
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 FROM_LOOP_C		__from_utf8_loop_c
Packit 6c4009
# define LOOPFCT		FROM_LOOP_C
Packit 6c4009
Packit 6c4009
# define LOOP_NEED_FLAGS
Packit 6c4009
Packit 6c4009
# define STORE_REST		STORE_REST_COMMON
Packit 6c4009
# define UNPACK_BYTES		UNPACK_BYTES_COMMON
Packit 6c4009
# define CLEAR_STATE		CLEAR_STATE_COMMON
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
#if HAVE_FROM_CU == 1
Packit 6c4009
/* This hardware routine uses the Convert UTF8 to UTF32 (cu14) instruction.  */
Packit 6c4009
# define BODY_FROM_ETF3EH BODY_FROM_HW (HARDWARE_CONVERT ("cu14 %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 FROM_LOOP_CU		__from_utf8_loop_etf3eh
Packit 6c4009
# define LOOPFCT		FROM_LOOP_CU
Packit 6c4009
Packit 6c4009
# define LOOP_NEED_FLAGS
Packit 6c4009
Packit 6c4009
# define STORE_REST		STORE_REST_COMMON
Packit 6c4009
# define UNPACK_BYTES		UNPACK_BYTES_COMMON
Packit 6c4009
# define CLEAR_STATE		CLEAR_STATE_COMMON
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_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],64,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 UCS4.  */				\
Packit 6c4009
		  "    vuplhb %%v18,%%v16\n\t"				\
Packit 6c4009
		  "    vupllb %%v19,%%v16\n\t"				\
Packit 6c4009
		  "    la %[R_IN],16(%[R_IN])\n\t"			\
Packit 6c4009
		  "    vuplhh %%v20,%%v18\n\t"				\
Packit 6c4009
		  "    aghi %[R_INLEN],-16\n\t"				\
Packit 6c4009
		  "    vupllh %%v21,%%v18\n\t"				\
Packit 6c4009
		  "    aghi %[R_OUTLEN],-64\n\t"			\
Packit 6c4009
		  "    vuplhh %%v22,%%v19\n\t"				\
Packit 6c4009
		  "    vupllh %%v23,%%v19\n\t"				\
Packit 6c4009
		  /* Store 64 bytes to buf_out.  */			\
Packit 6c4009
		  "    vstm %%v20,%%v23,0(%[R_OUT])\n\t"		\
Packit 6c4009
		  "    la %[R_OUT],64(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    clgijl %[R_INLEN],16,20f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],64,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],2\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
		  "    vuplhh %%v20,%%v18\n\t"				\
Packit 6c4009
		  "    vstl %%v20,%[R_TMP2],0(%[R_OUT])\n\t"		\
Packit 6c4009
		  "    ahi %[R_TMP2],-16\n\t"				\
Packit 6c4009
		  "    jl 11f\n\t"					\
Packit 6c4009
		  "    vupllh %%v21,%%v18\n\t"				\
Packit 6c4009
		  "    vstl %%v21,%[R_TMP2],16(%[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
		  "    vuplhh %%v22,%%v19\n\t"				\
Packit 6c4009
		  "    vstl %%v22,%[R_TMP2],32(%[R_OUT])\n\t"		\
Packit 6c4009
		  "    ahi %[R_TMP2],-16\n\t"				\
Packit 6c4009
		  "    jl 11f\n\t"					\
Packit 6c4009
		  "    vupllh %%v23,%%v19\n\t"				\
Packit 6c4009
		  "    vstl %%v23,%[R_TMP2],48(%[R_OUT])\n\t"		\
Packit 6c4009
		  "11: \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
		  /* Handle multibyte utf8-char with convert instruction. */ \
Packit 6c4009
		  "20: cu14 %[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 ("v20") ASM_CLOBBER_VR ("v21")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v22") ASM_CLOBBER_VR ("v30")	\
Packit 6c4009
		    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 FROM_LOOP_VX		__from_utf8_loop_vx
Packit 6c4009
# define LOOPFCT		FROM_LOOP_VX
Packit 6c4009
Packit 6c4009
# define LOOP_NEED_FLAGS
Packit 6c4009
Packit 6c4009
# define STORE_REST		STORE_REST_COMMON
Packit 6c4009
# define UNPACK_BYTES		UNPACK_BYTES_COMMON
Packit 6c4009
# define CLEAR_STATE		CLEAR_STATE_COMMON
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_TO_C == 1
Packit 6c4009
/* The software routine mimics the S/390 cu41 instruction.  */
Packit 6c4009
# define BODY_TO_C						\
Packit 6c4009
  {								\
Packit 6c4009
    uint32_t wc = *((const uint32_t *) inptr);			\
Packit 6c4009
								\
Packit 6c4009
    if (__glibc_likely (wc <= 0x7f))				\
Packit 6c4009
      {								\
Packit 6c4009
	/* Single UTF-8 char.  */				\
Packit 6c4009
	*outptr = (uint8_t)wc;					\
Packit 6c4009
	outptr++;						\
Packit 6c4009
      }								\
Packit 6c4009
    else if (wc <= 0x7ff)					\
Packit 6c4009
      {								\
Packit 6c4009
	/* Two UTF-8 chars.  */					\
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] |= wc >> 6;					\
Packit 6c4009
								\
Packit 6c4009
	outptr[1] = 0x80;					\
Packit 6c4009
	outptr[1] |= wc & 0x3f;					\
Packit 6c4009
								\
Packit 6c4009
	outptr += 2;						\
Packit 6c4009
      }								\
Packit 6c4009
    else if (wc <= 0xffff)					\
Packit 6c4009
      {								\
Packit 6c4009
	/* Three UTF-8 chars.  */				\
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
	if (wc >= 0xd800 && wc <= 0xdfff)			\
Packit 6c4009
	  {							\
Packit 6c4009
	    /* Do not accept UTF-16 surrogates.   */		\
Packit 6c4009
	    result = __GCONV_ILLEGAL_INPUT;			\
Packit 6c4009
	    STANDARD_TO_LOOP_ERR_HANDLER (4);			\
Packit 6c4009
	  }							\
Packit 6c4009
	outptr[0] = 0xe0;					\
Packit 6c4009
	outptr[0] |= wc >> 12;					\
Packit 6c4009
								\
Packit 6c4009
	outptr[1] = 0x80;					\
Packit 6c4009
	outptr[1] |= (wc >> 6) & 0x3f;				\
Packit 6c4009
								\
Packit 6c4009
	outptr[2] = 0x80;					\
Packit 6c4009
	outptr[2] |= wc & 0x3f;					\
Packit 6c4009
								\
Packit 6c4009
	outptr += 3;						\
Packit 6c4009
      }								\
Packit 6c4009
      else if (wc <= 0x10ffff)					\
Packit 6c4009
	{							\
Packit 6c4009
	  /* Four UTF-8 chars.  */				\
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
	  outptr[0] = 0xf0;					\
Packit 6c4009
	  outptr[0] |= wc >> 18;				\
Packit 6c4009
								\
Packit 6c4009
	  outptr[1] = 0x80;					\
Packit 6c4009
	  outptr[1] |= (wc >> 12) & 0x3f;			\
Packit 6c4009
								\
Packit 6c4009
	  outptr[2] = 0x80;					\
Packit 6c4009
	  outptr[2] |= (wc >> 6) & 0x3f;			\
Packit 6c4009
								\
Packit 6c4009
	  outptr[3] = 0x80;					\
Packit 6c4009
	  outptr[3] |= wc & 0x3f;				\
Packit 6c4009
								\
Packit 6c4009
	  outptr += 4;						\
Packit 6c4009
	}							\
Packit 6c4009
      else							\
Packit 6c4009
	{							\
Packit 6c4009
	  STANDARD_TO_LOOP_ERR_HANDLER (4);			\
Packit 6c4009
	}							\
Packit 6c4009
    inptr += 4;							\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
/* Generate loop-function with software routing.  */
Packit 6c4009
# define MIN_NEEDED_INPUT	MIN_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
/* The hardware routine uses the S/390 vector instructions.  */
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
		  "    vleif %%v20,127,0\n\t"   /* element 0: 127  */	\
Packit 6c4009
		  "    vzero %%v21\n\t"					\
Packit 6c4009
		  "    vleih %%v21,8192,0\n\t"  /* element 0:   >  */	\
Packit 6c4009
		  "    vleih %%v21,-8192,2\n\t" /* element 1: =<>  */	\
Packit 6c4009
		  CONVERT_32BIT_SIZE_T ([R_INLEN])			\
Packit 6c4009
		  CONVERT_32BIT_SIZE_T ([R_OUTLEN])			\
Packit 6c4009
		  /* Loop which handles UTF-32 chars <=0x7f.  */	\
Packit 6c4009
		  "0:  clgijl %[R_INLEN],64,2f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],16,2f\n\t"			\
Packit 6c4009
		  "1:  vlm %%v16,%%v19,0(%[R_IN])\n\t"			\
Packit 6c4009
		  "    lghi %[R_TMP2],0\n\t"				\
Packit 6c4009
		  /* Shorten to byte values.  */			\
Packit 6c4009
		  "    vpkf %%v23,%%v16,%%v17\n\t"			\
Packit 6c4009
		  "    vpkf %%v24,%%v18,%%v19\n\t"			\
Packit 6c4009
		  "    vpkh %%v23,%%v23,%%v24\n\t"			\
Packit 6c4009
		  /* Checking for values > 0x7f.  */			\
Packit 6c4009
		  "    vstrcfs %%v22,%%v16,%%v20,%%v21\n\t"		\
Packit 6c4009
		  "    jno 10f\n\t"					\
Packit 6c4009
		  "    vstrcfs %%v22,%%v17,%%v20,%%v21\n\t"		\
Packit 6c4009
		  "    jno 11f\n\t"					\
Packit 6c4009
		  "    vstrcfs %%v22,%%v18,%%v20,%%v21\n\t"		\
Packit 6c4009
		  "    jno 12f\n\t"					\
Packit 6c4009
		  "    vstrcfs %%v22,%%v19,%%v20,%%v21\n\t"		\
Packit 6c4009
		  "    jno 13f\n\t"					\
Packit 6c4009
		  /* Store 16bytes to outptr.  */			\
Packit 6c4009
		  "    vst %%v23,0(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    aghi %[R_INLEN],-64\n\t"				\
Packit 6c4009
		  "    aghi %[R_OUTLEN],-16\n\t"			\
Packit 6c4009
		  "    la %[R_IN],64(%[R_IN])\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],16(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    clgijl %[R_INLEN],64,2f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],16,2f\n\t"			\
Packit 6c4009
		  "    j 1b\n\t"					\
Packit 6c4009
		  /* Found a value > 0x7f.  */				\
Packit 6c4009
		  "13: ahi %[R_TMP2],4\n\t"				\
Packit 6c4009
		  "12: ahi %[R_TMP2],4\n\t"				\
Packit 6c4009
		  "11: ahi %[R_TMP2],4\n\t"				\
Packit 6c4009
		  "10: vlgvb %[R_TMP],%%v22,7\n\t"			\
Packit 6c4009
		  "    srlg %[R_TMP],%[R_TMP],2\n\t"			\
Packit 6c4009
		  "    agr %[R_TMP],%[R_TMP2]\n\t"			\
Packit 6c4009
		  "    je 16f\n\t"					\
Packit 6c4009
		  /* Store characters before invalid one...  */		\
Packit 6c4009
		  "    slgr %[R_OUTLEN],%[R_TMP]\n\t"			\
Packit 6c4009
		  "15: aghi %[R_TMP],-1\n\t"				\
Packit 6c4009
		  "    vstl %%v23,%[R_TMP],0(%[R_OUT])\n\t"		\
Packit 6c4009
		  /* ... and update pointers.  */			\
Packit 6c4009
		  "    aghi %[R_TMP],1\n\t"				\
Packit 6c4009
		  "    la %[R_OUT],0(%[R_TMP],%[R_OUT])\n\t"		\
Packit 6c4009
		  "    sllg %[R_TMP2],%[R_TMP],2\n\t"			\
Packit 6c4009
		  "    la %[R_IN],0(%[R_TMP2],%[R_IN])\n\t"		\
Packit 6c4009
		  "    slgr %[R_INLEN],%[R_TMP2]\n\t"			\
Packit 6c4009
		  /* Calculate remaining uint32_t values in loaded vrs.  */ \
Packit 6c4009
		  "16: lghi %[R_TMP2],16\n\t"				\
Packit 6c4009
		  "    sgr %[R_TMP2],%[R_TMP]\n\t"			\
Packit 6c4009
		  "    l %[R_TMP],0(%[R_IN])\n\t"			\
Packit 6c4009
		  "    aghi %[R_INLEN],-4\n\t"				\
Packit 6c4009
		  "    j 22f\n\t"					\
Packit 6c4009
		  /* Handle remaining bytes.  */			\
Packit 6c4009
		  "2:  clgije %[R_INLEN],0,99f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_INLEN],4,92f\n\t"			\
Packit 6c4009
		  /* Calculate remaining uint32_t values in inptr.  */	\
Packit 6c4009
		  "    srlg %[R_TMP2],%[R_INLEN],2\n\t"			\
Packit 6c4009
		  /* Handle multibyte utf8-char. */			\
Packit 6c4009
		  "20: l %[R_TMP],0(%[R_IN])\n\t"			\
Packit 6c4009
		  "    aghi %[R_INLEN],-4\n\t"				\
Packit 6c4009
		  /* Test if ch is 1byte 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],4(%[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 2byte 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
		  "    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_IN],4(%[R_IN])\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],0xffff\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
		  "    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
		  /* Test if ch is a UTF-16 surrogate: ch & 0xf800 == 0xd800  */ \
Packit 6c4009
		  "    nilf %[R_TMP],0xf800\n\t"			\
Packit 6c4009
		  "    clfi %[R_TMP],0xd800\n\t"			\
Packit 6c4009
		  "    je 91f\n\t" /* Do not accept UTF-16 surrogates.  */ \
Packit 6c4009
		  "    stcm %[R_TMP3],7,0(%[R_OUT])\n\t"		\
Packit 6c4009
		  "    la %[R_IN],4(%[R_IN])\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],0x10ffff\n\t"			\
Packit 6c4009
		  "    jh 91f\n\t" /* ch > 0x10ffff is not allowed!  */	\
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
		  "    llilf %[R_TMP3],0xf0808080\n\t"			\
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],37,39,6\n\t" /* 1. byte.  */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],42,47,4\n\t" /* 2. byte.  */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],50,55,2\n\t" /* 3. byte.  */ \
Packit 6c4009
		  "    risbgn %[R_TMP3],%[R_TMP],58,63,0\n\t" /* 4. byte.  */ \
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
		  "    brctg %[R_TMP2],20b\n\t"				\
Packit 6c4009
		  "    j 0b\n\t" /* Switch to vx-loop.  */		\
Packit 6c4009
		  "92: lghi %[R_RES],%[RES_IN_FULL]\n\t"		\
Packit 6c4009
		  "    j 99f\n\t"					\
Packit 6c4009
		  "91: lghi %[R_RES],%[RES_IN_ILL]\n\t"			\
Packit 6c4009
		  "    j 99f\n\t"					\
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] "=a" (tmp2), [R_TMP3] "=d" (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 ("v20") ASM_CLOBBER_VR ("v21")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v22") ASM_CLOBBER_VR ("v23")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v24")				\
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 (4);					\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
/* Generate loop-function with hardware vector instructions.  */
Packit 6c4009
# define MIN_NEEDED_INPUT	MIN_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;						\
Packit 6c4009
    asm volatile (".machine push\n\t"					\
Packit 6c4009
		  ".machine \"z13\"\n\t"				\
Packit 6c4009
		  ".machinemode \"zarch_nohighgprs\"\n\t"		\
Packit 6c4009
		  "    vleif %%v20,127,0\n\t"   /* element 0: 127  */	\
Packit 6c4009
		  "    vzero %%v21\n\t"					\
Packit 6c4009
		  "    vleih %%v21,8192,0\n\t"  /* element 0:   >  */	\
Packit 6c4009
		  "    vleih %%v21,-8192,2\n\t" /* element 1: =<>  */	\
Packit 6c4009
		  CONVERT_32BIT_SIZE_T ([R_INLEN])			\
Packit 6c4009
		  CONVERT_32BIT_SIZE_T ([R_OUTLEN])			\
Packit 6c4009
		  /* Loop which handles UTF-32 chars <= 0x7f.  */	\
Packit 6c4009
		  "0:  clgijl %[R_INLEN],64,20f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],16,20f\n\t"			\
Packit 6c4009
		  "1:  vlm %%v16,%%v19,0(%[R_IN])\n\t"			\
Packit 6c4009
		  "    lghi %[R_TMP],0\n\t"				\
Packit 6c4009
		  /* Shorten to byte values.  */			\
Packit 6c4009
		  "    vpkf %%v23,%%v16,%%v17\n\t"			\
Packit 6c4009
		  "    vpkf %%v24,%%v18,%%v19\n\t"			\
Packit 6c4009
		  "    vpkh %%v23,%%v23,%%v24\n\t"			\
Packit 6c4009
		  /* Checking for values > 0x7f.  */			\
Packit 6c4009
		  "    vstrcfs %%v22,%%v16,%%v20,%%v21\n\t"		\
Packit 6c4009
		  "    jno 10f\n\t"					\
Packit 6c4009
		  "    vstrcfs %%v22,%%v17,%%v20,%%v21\n\t"		\
Packit 6c4009
		  "    jno 11f\n\t"					\
Packit 6c4009
		  "    vstrcfs %%v22,%%v18,%%v20,%%v21\n\t"		\
Packit 6c4009
		  "    jno 12f\n\t"					\
Packit 6c4009
		  "    vstrcfs %%v22,%%v19,%%v20,%%v21\n\t"		\
Packit 6c4009
		  "    jno 13f\n\t"					\
Packit 6c4009
		  /* Store 16bytes to outptr.  */			\
Packit 6c4009
		  "    vst %%v23,0(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    aghi %[R_INLEN],-64\n\t"				\
Packit 6c4009
		  "    aghi %[R_OUTLEN],-16\n\t"			\
Packit 6c4009
		  "    la %[R_IN],64(%[R_IN])\n\t"			\
Packit 6c4009
		  "    la %[R_OUT],16(%[R_OUT])\n\t"			\
Packit 6c4009
		  "    clgijl %[R_INLEN],64,20f\n\t"			\
Packit 6c4009
		  "    clgijl %[R_OUTLEN],16,20f\n\t"			\
Packit 6c4009
		  "    j 1b\n\t"					\
Packit 6c4009
		  /* Found a value > 0x7f.  */				\
Packit 6c4009
		  "13: ahi %[R_TMP],4\n\t"				\
Packit 6c4009
		  "12: ahi %[R_TMP],4\n\t"				\
Packit 6c4009
		  "11: ahi %[R_TMP],4\n\t"				\
Packit 6c4009
		  "10: vlgvb %[R_I],%%v22,7\n\t"			\
Packit 6c4009
		  "    srlg %[R_I],%[R_I],2\n\t"			\
Packit 6c4009
		  "    agr %[R_I],%[R_TMP]\n\t"				\
Packit 6c4009
		  "    je 20f\n\t"					\
Packit 6c4009
		  /* Store characters before invalid one...  */		\
Packit 6c4009
		  "    slgr %[R_OUTLEN],%[R_I]\n\t"			\
Packit 6c4009
		  "15: aghi %[R_I],-1\n\t"				\
Packit 6c4009
		  "    vstl %%v23,%[R_I],0(%[R_OUT])\n\t"		\
Packit 6c4009
		  /* ... and update pointers.  */			\
Packit 6c4009
		  "    aghi %[R_I],1\n\t"				\
Packit 6c4009
		  "    la %[R_OUT],0(%[R_I],%[R_OUT])\n\t"		\
Packit 6c4009
		  "    sllg %[R_I],%[R_I],2\n\t"			\
Packit 6c4009
		  "    la %[R_IN],0(%[R_I],%[R_IN])\n\t"		\
Packit 6c4009
		  "    slgr %[R_INLEN],%[R_I]\n\t"			\
Packit 6c4009
		  /* Handle multibyte utf8-char with convert instruction. */ \
Packit 6c4009
		  "20: cu41 %[R_OUT],%[R_IN]\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] "=d" (tmp)	\
Packit 6c4009
		    , [R_I] "=a" (tmp2)					\
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 ("v20") ASM_CLOBBER_VR ("v21")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v22") ASM_CLOBBER_VR ("v23")	\
Packit 6c4009
		    ASM_CLOBBER_VR ("v24")				\
Packit 6c4009
		  );							\
Packit 6c4009
    inptr = pInput;							\
Packit 6c4009
    outptr = pOutput;							\
Packit 6c4009
									\
Packit 6c4009
    if (__glibc_likely (inptr == inend)					\
Packit 6c4009
	|| result == __GCONV_FULL_OUTPUT)				\
Packit 6c4009
      break;								\
Packit 6c4009
    if (inptr + 4 > inend)						\
Packit 6c4009
      {									\
Packit 6c4009
	result = __GCONV_INCOMPLETE_INPUT;				\
Packit 6c4009
	break;								\
Packit 6c4009
      }									\
Packit 6c4009
    STANDARD_TO_LOOP_ERR_HANDLER (4);					\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
/* Generate loop-function with hardware vector and utf-convert instructions.  */
Packit 6c4009
# define MIN_NEEDED_INPUT	MIN_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