Blame sysdeps/generic/memcopy.h

Packit 6c4009
/* memcopy.h -- definitions for memory copy functions.  Generic C version.
Packit 6c4009
   Copyright (C) 1991-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Torbjorn Granlund (tege@sics.se).
Packit 6c4009
Packit 6c4009
   The GNU C Library 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
   The GNU C Library 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
#ifndef _MEMCOPY_H
Packit 6c4009
#define _MEMCOPY_H	1
Packit 6c4009
Packit 6c4009
/* The strategy of the memory functions is:
Packit 6c4009
Packit 6c4009
     1. Copy bytes until the destination pointer is aligned.
Packit 6c4009
Packit 6c4009
     2. Copy words in unrolled loops.  If the source and destination
Packit 6c4009
     are not aligned in the same way, use word memory operations,
Packit 6c4009
     but shift and merge two read words before writing.
Packit 6c4009
Packit 6c4009
     3. Copy the few remaining bytes.
Packit 6c4009
Packit 6c4009
   This is fast on processors that have at least 10 registers for
Packit 6c4009
   allocation by GCC, and that can access memory at reg+const in one
Packit 6c4009
   instruction.
Packit 6c4009
Packit 6c4009
   I made an "exhaustive" test of this memmove when I wrote it,
Packit 6c4009
   exhaustive in the sense that I tried all alignment and length
Packit 6c4009
   combinations, with and without overlap.  */
Packit 6c4009
Packit 6c4009
#include <sys/cdefs.h>
Packit 6c4009
#include <endian.h>
Packit 6c4009
#include <pagecopy.h>
Packit 6c4009
Packit 6c4009
/* The macros defined in this file are:
Packit 6c4009
Packit 6c4009
   BYTE_COPY_FWD(dst_beg_ptr, src_beg_ptr, nbytes_to_copy)
Packit 6c4009
Packit 6c4009
   BYTE_COPY_BWD(dst_end_ptr, src_end_ptr, nbytes_to_copy)
Packit 6c4009
Packit 6c4009
   WORD_COPY_FWD(dst_beg_ptr, src_beg_ptr, nbytes_remaining, nbytes_to_copy)
Packit 6c4009
Packit 6c4009
   WORD_COPY_BWD(dst_end_ptr, src_end_ptr, nbytes_remaining, nbytes_to_copy)
Packit 6c4009
Packit 6c4009
   MERGE(old_word, sh_1, new_word, sh_2)
Packit 6c4009
     [I fail to understand.  I feel stupid.  --roland]
Packit 6c4009
*/
Packit 6c4009
Packit 6c4009
/* Type to use for aligned memory operations.
Packit 6c4009
   This should normally be the biggest type supported by a single load
Packit 6c4009
   and store.  */
Packit 6c4009
#define	op_t	unsigned long int
Packit 6c4009
#define OPSIZ	(sizeof(op_t))
Packit 6c4009
Packit 6c4009
/* Type to use for unaligned operations.  */
Packit 6c4009
typedef unsigned char byte;
Packit 6c4009
Packit 6c4009
#if __BYTE_ORDER == __LITTLE_ENDIAN
Packit 6c4009
#define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2)))
Packit 6c4009
#endif
Packit 6c4009
#if __BYTE_ORDER == __BIG_ENDIAN
Packit 6c4009
#define MERGE(w0, sh_1, w1, sh_2) (((w0) << (sh_1)) | ((w1) >> (sh_2)))
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Copy exactly NBYTES bytes from SRC_BP to DST_BP,
Packit 6c4009
   without any assumptions about alignment of the pointers.  */
Packit 6c4009
#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes)				      \
Packit 6c4009
  do									      \
Packit 6c4009
    {									      \
Packit 6c4009
      size_t __nbytes = (nbytes);					      \
Packit 6c4009
      while (__nbytes > 0)						      \
Packit 6c4009
	{								      \
Packit 6c4009
	  byte __x = ((byte *) src_bp)[0];				      \
Packit 6c4009
	  src_bp += 1;							      \
Packit 6c4009
	  __nbytes -= 1;						      \
Packit 6c4009
	  ((byte *) dst_bp)[0] = __x;					      \
Packit 6c4009
	  dst_bp += 1;							      \
Packit 6c4009
	}								      \
Packit 6c4009
    } while (0)
Packit 6c4009
Packit 6c4009
/* Copy exactly NBYTES_TO_COPY bytes from SRC_END_PTR to DST_END_PTR,
Packit 6c4009
   beginning at the bytes right before the pointers and continuing towards
Packit 6c4009
   smaller addresses.  Don't assume anything about alignment of the
Packit 6c4009
   pointers.  */
Packit 6c4009
#define BYTE_COPY_BWD(dst_ep, src_ep, nbytes)				      \
Packit 6c4009
  do									      \
Packit 6c4009
    {									      \
Packit 6c4009
      size_t __nbytes = (nbytes);					      \
Packit 6c4009
      while (__nbytes > 0)						      \
Packit 6c4009
	{								      \
Packit 6c4009
	  byte __x;							      \
Packit 6c4009
	  src_ep -= 1;							      \
Packit 6c4009
	  __x = ((byte *) src_ep)[0];					      \
Packit 6c4009
	  dst_ep -= 1;							      \
Packit 6c4009
	  __nbytes -= 1;						      \
Packit 6c4009
	  ((byte *) dst_ep)[0] = __x;					      \
Packit 6c4009
	}								      \
Packit 6c4009
    } while (0)
Packit 6c4009
Packit 6c4009
/* Copy *up to* NBYTES bytes from SRC_BP to DST_BP, with
Packit 6c4009
   the assumption that DST_BP is aligned on an OPSIZ multiple.  If
Packit 6c4009
   not all bytes could be easily copied, store remaining number of bytes
Packit 6c4009
   in NBYTES_LEFT, otherwise store 0.  */
Packit 6c4009
extern void _wordcopy_fwd_aligned (long int, long int, size_t)
Packit 6c4009
  attribute_hidden __THROW;
Packit 6c4009
extern void _wordcopy_fwd_dest_aligned (long int, long int, size_t)
Packit 6c4009
  attribute_hidden __THROW;
Packit 6c4009
#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes)		      \
Packit 6c4009
  do									      \
Packit 6c4009
    {									      \
Packit 6c4009
      if (src_bp % OPSIZ == 0)						      \
Packit 6c4009
	_wordcopy_fwd_aligned (dst_bp, src_bp, (nbytes) / OPSIZ);	      \
Packit 6c4009
      else								      \
Packit 6c4009
	_wordcopy_fwd_dest_aligned (dst_bp, src_bp, (nbytes) / OPSIZ);	      \
Packit 6c4009
      src_bp += (nbytes) & -OPSIZ;					      \
Packit 6c4009
      dst_bp += (nbytes) & -OPSIZ;					      \
Packit 6c4009
      (nbytes_left) = (nbytes) % OPSIZ;					      \
Packit 6c4009
    } while (0)
Packit 6c4009
Packit 6c4009
/* Copy *up to* NBYTES_TO_COPY bytes from SRC_END_PTR to DST_END_PTR,
Packit 6c4009
   beginning at the words (of type op_t) right before the pointers and
Packit 6c4009
   continuing towards smaller addresses.  May take advantage of that
Packit 6c4009
   DST_END_PTR is aligned on an OPSIZ multiple.  If not all bytes could be
Packit 6c4009
   easily copied, store remaining number of bytes in NBYTES_REMAINING,
Packit 6c4009
   otherwise store 0.  */
Packit 6c4009
extern void _wordcopy_bwd_aligned (long int, long int, size_t)
Packit 6c4009
  attribute_hidden __THROW;
Packit 6c4009
extern void _wordcopy_bwd_dest_aligned (long int, long int, size_t)
Packit 6c4009
  attribute_hidden __THROW;
Packit 6c4009
#define WORD_COPY_BWD(dst_ep, src_ep, nbytes_left, nbytes)		      \
Packit 6c4009
  do									      \
Packit 6c4009
    {									      \
Packit 6c4009
      if (src_ep % OPSIZ == 0)						      \
Packit 6c4009
	_wordcopy_bwd_aligned (dst_ep, src_ep, (nbytes) / OPSIZ);	      \
Packit 6c4009
      else								      \
Packit 6c4009
	_wordcopy_bwd_dest_aligned (dst_ep, src_ep, (nbytes) / OPSIZ);	      \
Packit 6c4009
      src_ep -= (nbytes) & -OPSIZ;					      \
Packit 6c4009
      dst_ep -= (nbytes) & -OPSIZ;					      \
Packit 6c4009
      (nbytes_left) = (nbytes) % OPSIZ;					      \
Packit 6c4009
    } while (0)
Packit 6c4009
Packit 6c4009
/* The macro PAGE_COPY_FWD_MAYBE (dstp, srcp, nbytes_left, nbytes) is invoked
Packit 6c4009
   like WORD_COPY_FWD et al.  The pointers should be at least word aligned.
Packit 6c4009
   This will check if virtual copying by pages can and should be done and do it
Packit 6c4009
   if so.  The pointers will be aligned to PAGE_SIZE bytes.  The macro requires
Packit 6c4009
   that pagecopy.h defines at least PAGE_COPY_THRESHOLD to 0.  If
Packit 6c4009
   PAGE_COPY_THRESHOLD is non-zero, the header must also define PAGE_COPY_FWD
Packit 6c4009
   and PAGE_SIZE.
Packit 6c4009
*/
Packit 6c4009
#if PAGE_COPY_THRESHOLD
Packit 6c4009
Packit 6c4009
# include <assert.h>
Packit 6c4009
Packit 6c4009
# define PAGE_COPY_FWD_MAYBE(dstp, srcp, nbytes_left, nbytes)		      \
Packit 6c4009
  do									      \
Packit 6c4009
    {									      \
Packit 6c4009
      if ((nbytes) >= PAGE_COPY_THRESHOLD &&				      \
Packit 6c4009
	  PAGE_OFFSET ((dstp) - (srcp)) == 0) 				      \
Packit 6c4009
	{								      \
Packit 6c4009
	  /* The amount to copy is past the threshold for copying	      \
Packit 6c4009
	     pages virtually with kernel VM operations, and the		      \
Packit 6c4009
	     source and destination addresses have the same alignment.  */    \
Packit 6c4009
	  size_t nbytes_before = PAGE_OFFSET (-(dstp));			      \
Packit 6c4009
	  if (nbytes_before != 0)					      \
Packit 6c4009
	    {								      \
Packit 6c4009
	      /* First copy the words before the first page boundary.  */     \
Packit 6c4009
	      WORD_COPY_FWD (dstp, srcp, nbytes_left, nbytes_before);	      \
Packit 6c4009
	      assert (nbytes_left == 0);				      \
Packit 6c4009
	      nbytes -= nbytes_before;					      \
Packit 6c4009
	    }								      \
Packit 6c4009
	  PAGE_COPY_FWD (dstp, srcp, nbytes_left, nbytes);		      \
Packit 6c4009
	}								      \
Packit 6c4009
    } while (0)
Packit 6c4009
Packit 6c4009
/* The page size is always a power of two, so we can avoid modulo division.  */
Packit 6c4009
# define PAGE_OFFSET(n)	((n) & (PAGE_SIZE - 1))
Packit 6c4009
Packit 6c4009
#else
Packit 6c4009
Packit 6c4009
# define PAGE_COPY_FWD_MAYBE(dstp, srcp, nbytes_left, nbytes) /* nada */
Packit 6c4009
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Threshold value for when to enter the unrolled loops.  */
Packit 6c4009
#define	OP_T_THRES	16
Packit 6c4009
Packit 6c4009
/* Set to 1 if memcpy is safe to use for forward-copying memmove with
Packit 6c4009
   overlapping addresses.  This is 0 by default because memcpy implementations
Packit 6c4009
   are generally not safe for overlapping addresses.  */
Packit 6c4009
#define MEMCPY_OK_FOR_FWD_MEMMOVE 0
Packit 6c4009
Packit 6c4009
#endif /* memcopy.h */