iconv: Remove _STRING_ARCH_unaligned usage

Use put/get macros __builtin_bswap32 instead.  It allows to remove
the unaligned routines, the compiler will generate unaligned access
if the ABI allows it.

Checked on x86_64-linux-gnu and i686-linux-gnu.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
This commit is contained in:
Adhemerval Zanella 2023-02-10 16:37:36 -03:00
parent 5729e0e9af
commit 3e20ddade3
3 changed files with 59 additions and 407 deletions

View File

@ -86,13 +86,15 @@ internal_ucs4_loop (struct __gconv_step *step,
#if __BYTE_ORDER == __LITTLE_ENDIAN
/* Sigh, we have to do some real work. */
size_t cnt;
uint32_t *outptr32 = (uint32_t *) outptr;
for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4)
*outptr32++ = bswap_32 (*(const uint32_t *) inptr);
for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4, outptr += 4)
{
uint32_t val = get32 (inptr);
put32 (outptr, __builtin_bswap32 (val));
}
*inptrp = inptr;
*outptrp = (unsigned char *) outptr32;
*outptrp = outptr;
#elif __BYTE_ORDER == __BIG_ENDIAN
/* Simply copy the data. */
*inptrp = inptr + n_convert * 4;
@ -112,56 +114,6 @@ internal_ucs4_loop (struct __gconv_step *step,
return result;
}
#if !_STRING_ARCH_unaligned
static inline int
__attribute ((always_inline))
internal_ucs4_loop_unaligned (struct __gconv_step *step,
struct __gconv_step_data *step_data,
const unsigned char **inptrp,
const unsigned char *inend,
unsigned char **outptrp,
const unsigned char *outend,
size_t *irreversible)
{
const unsigned char *inptr = *inptrp;
unsigned char *outptr = *outptrp;
size_t n_convert = MIN (inend - inptr, outend - outptr) / 4;
int result;
# if __BYTE_ORDER == __LITTLE_ENDIAN
/* Sigh, we have to do some real work. */
size_t cnt;
for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4, outptr += 4)
{
outptr[0] = inptr[3];
outptr[1] = inptr[2];
outptr[2] = inptr[1];
outptr[3] = inptr[0];
}
*inptrp = inptr;
*outptrp = outptr;
# elif __BYTE_ORDER == __BIG_ENDIAN
/* Simply copy the data. */
*inptrp = inptr + n_convert * 4;
*outptrp = __mempcpy (outptr, inptr, n_convert * 4);
# else
# error "This endianess is not supported."
# endif
/* Determine the status. */
if (*inptrp == inend)
result = __GCONV_EMPTY_INPUT;
else if (*outptrp + 4 > outend)
result = __GCONV_FULL_OUTPUT;
else
result = __GCONV_INCOMPLETE_INPUT;
return result;
}
#endif
static inline int
__attribute ((always_inline))
@ -242,12 +194,9 @@ ucs4_internal_loop (struct __gconv_step *step,
for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4)
{
uint32_t inval;
uint32_t inval = get32 (inptr);
#if __BYTE_ORDER == __LITTLE_ENDIAN
inval = bswap_32 (*(const uint32_t *) inptr);
#else
inval = *(const uint32_t *) inptr;
inval = __builtin_bswap32 (inval);
#endif
if (__glibc_unlikely (inval > 0x7fffffff))
@ -272,7 +221,7 @@ ucs4_internal_loop (struct __gconv_step *step,
return __GCONV_ILLEGAL_INPUT;
}
*((uint32_t *) outptr) = inval;
put32 (outptr, inval);
outptr += sizeof (uint32_t);
}
@ -290,75 +239,6 @@ ucs4_internal_loop (struct __gconv_step *step,
return result;
}
#if !_STRING_ARCH_unaligned
static inline int
__attribute ((always_inline))
ucs4_internal_loop_unaligned (struct __gconv_step *step,
struct __gconv_step_data *step_data,
const unsigned char **inptrp,
const unsigned char *inend,
unsigned char **outptrp,
const unsigned char *outend,
size_t *irreversible)
{
int flags = step_data->__flags;
const unsigned char *inptr = *inptrp;
unsigned char *outptr = *outptrp;
int result;
for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4)
{
if (__glibc_unlikely (inptr[0] > 0x80))
{
/* The value is too large. We don't try transliteration here since
this is not an error because of the lack of possibilities to
represent the result. This is a genuine bug in the input since
UCS4 does not allow such values. */
if (irreversible == NULL)
/* We are transliterating, don't try to correct anything. */
return __GCONV_ILLEGAL_INPUT;
if (flags & __GCONV_IGNORE_ERRORS)
{
/* Just ignore this character. */
++*irreversible;
continue;
}
*inptrp = inptr;
*outptrp = outptr;
return __GCONV_ILLEGAL_INPUT;
}
# if __BYTE_ORDER == __LITTLE_ENDIAN
outptr[3] = inptr[0];
outptr[2] = inptr[1];
outptr[1] = inptr[2];
outptr[0] = inptr[3];
# else
outptr[0] = inptr[0];
outptr[1] = inptr[1];
outptr[2] = inptr[2];
outptr[3] = inptr[3];
# endif
outptr += 4;
}
*inptrp = inptr;
*outptrp = outptr;
/* Determine the status. */
if (*inptrp == inend)
result = __GCONV_EMPTY_INPUT;
else if (*outptrp + 4 > outend)
result = __GCONV_FULL_OUTPUT;
else
result = __GCONV_INCOMPLETE_INPUT;
return result;
}
#endif
static inline int
__attribute ((always_inline))
@ -453,11 +333,12 @@ internal_ucs4le_loop (struct __gconv_step *step,
#if __BYTE_ORDER == __BIG_ENDIAN
/* Sigh, we have to do some real work. */
size_t cnt;
uint32_t *outptr32 = (uint32_t *) outptr;
for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4)
*outptr32++ = bswap_32 (*(const uint32_t *) inptr);
outptr = (unsigned char *) outptr32;
for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4, outptr += 4)
{
uint32_t val = get32 (inptr);
put32 (outptr, __builtin_bswap32 (val));
}
*inptrp = inptr;
*outptrp = outptr;
@ -480,59 +361,6 @@ internal_ucs4le_loop (struct __gconv_step *step,
return result;
}
#if !_STRING_ARCH_unaligned
static inline int
__attribute ((always_inline))
internal_ucs4le_loop_unaligned (struct __gconv_step *step,
struct __gconv_step_data *step_data,
const unsigned char **inptrp,
const unsigned char *inend,
unsigned char **outptrp,
const unsigned char *outend,
size_t *irreversible)
{
const unsigned char *inptr = *inptrp;
unsigned char *outptr = *outptrp;
size_t n_convert = MIN (inend - inptr, outend - outptr) / 4;
int result;
# if __BYTE_ORDER == __BIG_ENDIAN
/* Sigh, we have to do some real work. */
size_t cnt;
for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4, outptr += 4)
{
outptr[0] = inptr[3];
outptr[1] = inptr[2];
outptr[2] = inptr[1];
outptr[3] = inptr[0];
}
*inptrp = inptr;
*outptrp = outptr;
# elif __BYTE_ORDER == __LITTLE_ENDIAN
/* Simply copy the data. */
*inptrp = inptr + n_convert * 4;
*outptrp = __mempcpy (outptr, inptr, n_convert * 4);
# else
# error "This endianess is not supported."
# endif
/* Determine the status. */
if (*inptrp == inend)
result = __GCONV_EMPTY_INPUT;
else if (*inptrp + 4 > inend)
result = __GCONV_INCOMPLETE_INPUT;
else
{
assert (*outptrp + 4 > outend);
result = __GCONV_FULL_OUTPUT;
}
return result;
}
#endif
static inline int
__attribute ((always_inline))
@ -612,12 +440,9 @@ ucs4le_internal_loop (struct __gconv_step *step,
for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4)
{
uint32_t inval;
uint32_t inval = get32 (inptr);
#if __BYTE_ORDER == __BIG_ENDIAN
inval = bswap_32 (*(const uint32_t *) inptr);
#else
inval = *(const uint32_t *) inptr;
inval = __builtin_bswap32 (inval);
#endif
if (__glibc_unlikely (inval > 0x7fffffff))
@ -642,7 +467,7 @@ ucs4le_internal_loop (struct __gconv_step *step,
return __GCONV_ILLEGAL_INPUT;
}
*((uint32_t *) outptr) = inval;
put32 (outptr, inval);
outptr += sizeof (uint32_t);
}
@ -663,79 +488,6 @@ ucs4le_internal_loop (struct __gconv_step *step,
return result;
}
#if !_STRING_ARCH_unaligned
static inline int
__attribute ((always_inline))
ucs4le_internal_loop_unaligned (struct __gconv_step *step,
struct __gconv_step_data *step_data,
const unsigned char **inptrp,
const unsigned char *inend,
unsigned char **outptrp,
const unsigned char *outend,
size_t *irreversible)
{
int flags = step_data->__flags;
const unsigned char *inptr = *inptrp;
unsigned char *outptr = *outptrp;
int result;
for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4)
{
if (__glibc_unlikely (inptr[3] > 0x80))
{
/* The value is too large. We don't try transliteration here since
this is not an error because of the lack of possibilities to
represent the result. This is a genuine bug in the input since
UCS4 does not allow such values. */
if (irreversible == NULL)
/* We are transliterating, don't try to correct anything. */
return __GCONV_ILLEGAL_INPUT;
if (flags & __GCONV_IGNORE_ERRORS)
{
/* Just ignore this character. */
++*irreversible;
continue;
}
*inptrp = inptr;
*outptrp = outptr;
return __GCONV_ILLEGAL_INPUT;
}
# if __BYTE_ORDER == __BIG_ENDIAN
outptr[3] = inptr[0];
outptr[2] = inptr[1];
outptr[1] = inptr[2];
outptr[0] = inptr[3];
# else
outptr[0] = inptr[0];
outptr[1] = inptr[1];
outptr[2] = inptr[2];
outptr[3] = inptr[3];
# endif
outptr += 4;
}
*inptrp = inptr;
*outptrp = outptr;
/* Determine the status. */
if (*inptrp == inend)
result = __GCONV_EMPTY_INPUT;
else if (*inptrp + 4 > inend)
result = __GCONV_INCOMPLETE_INPUT;
else
{
assert (*outptrp + 4 > outend);
result = __GCONV_FULL_OUTPUT;
}
return result;
}
#endif
static inline int
__attribute ((always_inline))

View File

@ -58,12 +58,7 @@
#include <libc-diag.h>
#undef FCTNAME2
#if _STRING_ARCH_unaligned || !defined DEFINE_UNALIGNED
# define FCTNAME2(name) name
#else
# define FCTNAME2(name) name##_unaligned
#endif
#define FCTNAME(name) FCTNAME2(name)
#define FCTNAME(name) name
/* We need at least one byte for the next round. */
@ -279,18 +274,7 @@ FCTNAME (LOOPFCT) (struct __gconv_step *step,
}
/* Include the file a second time to define the function to handle
unaligned access. */
#if !defined DEFINE_UNALIGNED && !_STRING_ARCH_unaligned \
&& MIN_NEEDED_INPUT != 1 && MAX_NEEDED_INPUT % MIN_NEEDED_INPUT == 0 \
&& MIN_NEEDED_OUTPUT != 1 && MAX_NEEDED_OUTPUT % MIN_NEEDED_OUTPUT == 0
# undef unaligned
# define DEFINE_UNALIGNED
# include "loop.c"
# undef DEFINE_UNALIGNED
#else
# if MAX_NEEDED_INPUT > 1
#if MAX_NEEDED_INPUT > 1
# define SINGLE(fct) SINGLE2 (fct)
# define SINGLE2(fct) fct##_single
static inline int
@ -439,7 +423,6 @@ SINGLE(LOOPFCT) (struct __gconv_step *step,
}
# undef SINGLE
# undef SINGLE2
# endif
# ifdef ONEBYTE_BODY
@ -471,4 +454,3 @@ gconv_btowc (struct __gconv_step *step, unsigned char c)
#undef LOOP_NEED_STATE
#undef LOOP_NEED_FLAGS
#undef LOOP_NEED_DATA
#undef unaligned

View File

@ -448,33 +448,6 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
size_t lirreversible = 0;
size_t *lirreversiblep = irreversible ? &lirreversible : NULL;
/* The following assumes that encodings, which have a variable length
what might unalign a buffer even though it is an aligned in the
beginning, either don't have the minimal number of bytes as a divisor
of the maximum length or have a minimum length of 1. This is true
for all known and supported encodings.
We use && instead of || to combine the subexpression for the FROM
encoding and for the TO encoding, because usually one of them is
INTERNAL, for which the subexpression evaluates to 1, but INTERNAL
buffers are always aligned correctly. */
#define POSSIBLY_UNALIGNED \
(!_STRING_ARCH_unaligned \
&& (((FROM_LOOP_MIN_NEEDED_FROM != 1 \
&& FROM_LOOP_MAX_NEEDED_FROM % FROM_LOOP_MIN_NEEDED_FROM == 0) \
&& (FROM_LOOP_MIN_NEEDED_TO != 1 \
&& FROM_LOOP_MAX_NEEDED_TO % FROM_LOOP_MIN_NEEDED_TO == 0)) \
|| ((TO_LOOP_MIN_NEEDED_FROM != 1 \
&& TO_LOOP_MAX_NEEDED_FROM % TO_LOOP_MIN_NEEDED_FROM == 0) \
&& (TO_LOOP_MIN_NEEDED_TO != 1 \
&& TO_LOOP_MAX_NEEDED_TO % TO_LOOP_MIN_NEEDED_TO == 0))))
#if POSSIBLY_UNALIGNED
int unaligned;
# define GEN_unaligned(name) GEN_unaligned2 (name)
# define GEN_unaligned2(name) name##_unaligned
#else
# define unaligned 0
#endif
#ifdef PREPARE_LOOP
PREPARE_LOOP
#endif
@ -514,18 +487,6 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
}
#endif
#if POSSIBLY_UNALIGNED
unaligned =
((FROM_DIRECTION
&& ((uintptr_t) inptr % FROM_LOOP_MIN_NEEDED_FROM != 0
|| ((data->__flags & __GCONV_IS_LAST)
&& (uintptr_t) outbuf % FROM_LOOP_MIN_NEEDED_TO != 0)))
|| (!FROM_DIRECTION
&& (((data->__flags & __GCONV_IS_LAST)
&& (uintptr_t) outbuf % TO_LOOP_MIN_NEEDED_TO != 0)
|| (uintptr_t) inptr % TO_LOOP_MIN_NEEDED_FROM != 0)));
#endif
while (1)
{
/* Remember the start value for this round. */
@ -543,8 +504,6 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
SAVE_RESET_STATE (1);
#endif
if (__glibc_likely (!unaligned))
{
if (FROM_DIRECTION)
/* Run the conversion loop. */
status = FROM_LOOP (step, data, inptrp, inend, &outbuf, outend,
@ -553,24 +512,6 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
/* Run the conversion loop. */
status = TO_LOOP (step, data, inptrp, inend, &outbuf, outend,
lirreversiblep EXTRA_LOOP_ARGS);
}
#if POSSIBLY_UNALIGNED
else
{
if (FROM_DIRECTION)
/* Run the conversion loop. */
status = GEN_unaligned (FROM_LOOP) (step, data, inptrp, inend,
&outbuf, outend,
lirreversiblep
EXTRA_LOOP_ARGS);
else
/* Run the conversion loop. */
status = GEN_unaligned (TO_LOOP) (step, data, inptrp, inend,
&outbuf, outend,
lirreversiblep
EXTRA_LOOP_ARGS);
}
#endif
/* If we were called as part of an error handling module we
don't do anything else here. */
@ -635,8 +576,6 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
SAVE_RESET_STATE (0);
#endif
if (__glibc_likely (!unaligned))
{
if (FROM_DIRECTION)
/* Run the conversion loop. */
nstatus = FROM_LOOP (step, data, inptrp, inend,
@ -649,27 +588,6 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
&outbuf, outerr,
lirreversiblep
EXTRA_LOOP_ARGS);
}
#if POSSIBLY_UNALIGNED
else
{
if (FROM_DIRECTION)
/* Run the conversion loop. */
nstatus = GEN_unaligned (FROM_LOOP) (step, data,
inptrp, inend,
&outbuf,
outerr,
lirreversiblep
EXTRA_LOOP_ARGS);
else
/* Run the conversion loop. */
nstatus = GEN_unaligned (TO_LOOP) (step, data,
inptrp, inend,
&outbuf, outerr,
lirreversiblep
EXTRA_LOOP_ARGS);
}
#endif
/* We must run out of output buffer space in this
rerun. */