diff options
author | David Crocker <dcrocker@eschertech.com> | 2019-12-24 18:50:13 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2019-12-24 18:50:13 +0300 |
commit | 6a5589d5146ff86151d82e8d8c9c822ebc085271 (patch) | |
tree | 806f63c7b4d98c6436510b02e12061dee81868be /src/libc | |
parent | be8bf5da39b36e46d769b93e4f423d347abc23ba (diff) |
Fixes to exception support for Duet 3
Diffstat (limited to 'src/libc')
-rw-r--r-- | src/libc/local.h | 23 | ||||
-rw-r--r-- | src/libc/memcmp.c | 105 | ||||
-rw-r--r-- | src/libc/memcpy.c | 105 | ||||
-rw-r--r-- | src/libc/memmove.c | 136 | ||||
-rw-r--r-- | src/libc/memset.c | 96 | ||||
-rw-r--r-- | src/libc/readme-libc.txt | 8 |
6 files changed, 473 insertions, 0 deletions
diff --git a/src/libc/local.h b/src/libc/local.h new file mode 100644 index 00000000..c4bf6af8 --- /dev/null +++ b/src/libc/local.h @@ -0,0 +1,23 @@ +#include <_ansi.h> + +#if 0 // dc42 +#include <../ctype/local.h> + +/* internal function to compute width of wide char. */ +int __wcwidth (wint_t); +#endif + +/* + Taken from glibc: + Add the compiler optimization to inhibit loop transformation to library + calls. This is used to avoid recursive calls in memset and memmove + default implementations. +*/ +#ifdef _HAVE_CC_INHIBIT_LOOP_TO_LIBCALL +# define __inhibit_loop_to_libcall \ + __attribute__ ((__optimize__ ("-fno-tree-loop-distribute-patterns"))) +#else +# define __inhibit_loop_to_libcall +#endif + + diff --git a/src/libc/memcmp.c b/src/libc/memcmp.c new file mode 100644 index 00000000..342fb9fb --- /dev/null +++ b/src/libc/memcmp.c @@ -0,0 +1,105 @@ +/* +FUNCTION + <<memcmp>>---compare two memory areas + +INDEX + memcmp + +SYNOPSIS + #include <string.h> + int memcmp(const void *<[s1]>, const void *<[s2]>, size_t <[n]>); + +DESCRIPTION + This function compares not more than <[n]> characters of the + object pointed to by <[s1]> with the object pointed to by <[s2]>. + + +RETURNS + The function returns an integer greater than, equal to or + less than zero according to whether the object pointed to by + <[s1]> is greater than, equal to or less than the object + pointed to by <[s2]>. + +PORTABILITY +<<memcmp>> is ANSI C. + +<<memcmp>> requires no supporting OS subroutines. + +QUICKREF + memcmp ansi pure +*/ + +#include <string.h> + + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +int +memcmp (const void *m1, + const void *m2, + size_t n) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + unsigned char *s1 = (unsigned char *) m1; + unsigned char *s2 = (unsigned char *) m2; + + while (n--) + { + if (*s1 != *s2) + { + return *s1 - *s2; + } + s1++; + s2++; + } + return 0; +#else + unsigned char *s1 = (unsigned char *) m1; + unsigned char *s2 = (unsigned char *) m2; + unsigned long *a1; + unsigned long *a2; + + /* If the size is too small, or either pointer is unaligned, + then we punt to the byte compare loop. Hopefully this will + not turn up in inner loops. */ + if (!TOO_SMALL(n) && !UNALIGNED(s1,s2)) + { + /* Otherwise, load and compare the blocks of memory one + word at a time. */ + a1 = (unsigned long*) s1; + a2 = (unsigned long*) s2; + while (n >= LBLOCKSIZE) + { + if (*a1 != *a2) + break; + a1++; + a2++; + n -= LBLOCKSIZE; + } + + /* check m mod LBLOCKSIZE remaining characters */ + + s1 = (unsigned char*)a1; + s2 = (unsigned char*)a2; + } + + while (n--) + { + if (*s1 != *s2) + return *s1 - *s2; + s1++; + s2++; + } + + return 0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + diff --git a/src/libc/memcpy.c b/src/libc/memcpy.c new file mode 100644 index 00000000..52f716b9 --- /dev/null +++ b/src/libc/memcpy.c @@ -0,0 +1,105 @@ +/* +FUNCTION + <<memcpy>>---copy memory regions + +SYNOPSIS + #include <string.h> + void* memcpy(void *restrict <[out]>, const void *restrict <[in]>, + size_t <[n]>); + +DESCRIPTION + This function copies <[n]> bytes from the memory region + pointed to by <[in]> to the memory region pointed to by + <[out]>. + + If the regions overlap, the behavior is undefined. + +RETURNS + <<memcpy>> returns a pointer to the first byte of the <[out]> + region. + +PORTABILITY +<<memcpy>> is ANSI C. + +<<memcpy>> requires no supporting OS subroutines. + +QUICKREF + memcpy ansi pure + */ + +#include <_ansi.h> +#include <string.h> +#include "local.h" + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the 4X unrolled loop. */ +#define BIGBLOCKSIZE (sizeof (long) << 2) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LITTLEBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) + +void * +__inhibit_loop_to_libcall +memcpy (void *__restrict dst0, + const void *__restrict src0, + size_t len0) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dst = (char *) dst0; + char *src = (char *) src0; + + void *save = dst0; + + while (len0--) + { + *dst++ = *src++; + } + + return save; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If the size is small, or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(len0) && !UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* Copy 4X long words at a time if possible. */ + while (len0 >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + len0 -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (len0 >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + len0 -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (len0--) + *dst++ = *src++; + + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} diff --git a/src/libc/memmove.c b/src/libc/memmove.c new file mode 100644 index 00000000..da5dfdbd --- /dev/null +++ b/src/libc/memmove.c @@ -0,0 +1,136 @@ +/* +FUNCTION + <<memmove>>---move possibly overlapping memory + +INDEX + memmove + +SYNOPSIS + #include <string.h> + void *memmove(void *<[dst]>, const void *<[src]>, size_t <[length]>); + +DESCRIPTION + This function moves <[length]> characters from the block of + memory starting at <<*<[src]>>> to the memory starting at + <<*<[dst]>>>. <<memmove>> reproduces the characters correctly + at <<*<[dst]>>> even if the two areas overlap. + + +RETURNS + The function returns <[dst]> as passed. + +PORTABILITY +<<memmove>> is ANSI C. + +<<memmove>> requires no supporting OS subroutines. + +QUICKREF + memmove ansi pure +*/ + +#include <string.h> +#include <_ansi.h> +#include <stddef.h> +#include <limits.h> +#include "local.h" + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the 4X unrolled loop. */ +#define BIGBLOCKSIZE (sizeof (long) << 2) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LITTLEBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) + +/*SUPPRESS 20*/ +void * +__inhibit_loop_to_libcall +memmove (void *dst_void, + const void *src_void, + size_t length) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dst = dst_void; + const char *src = src_void; + + if (src < dst && dst < src + length) + { + /* Have to copy backwards */ + src += length; + dst += length; + while (length--) + { + *--dst = *--src; + } + } + else + { + while (length--) + { + *dst++ = *src++; + } + } + + return dst_void; +#else + char *dst = dst_void; + const char *src = src_void; + long *aligned_dst; + const long *aligned_src; + + if (src < dst && dst < src + length) + { + /* Destructive overlap...have to copy backwards */ + src += length; + dst += length; + while (length--) + { + *--dst = *--src; + } + } + else + { + /* Use optimizing algorithm for a non-destructive copy to closely + match memcpy. If the size is small or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(length) && !UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* Copy 4X long words at a time if possible. */ + while (length >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + length -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (length >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + length -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (length--) + { + *dst++ = *src++; + } + } + + return dst_void; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} diff --git a/src/libc/memset.c b/src/libc/memset.c new file mode 100644 index 00000000..e8e667a2 --- /dev/null +++ b/src/libc/memset.c @@ -0,0 +1,96 @@ +/* +FUNCTION + <<memset>>---set an area of memory + +INDEX + memset + +SYNOPSIS + #include <string.h> + void *memset(void *<[dst]>, int <[c]>, size_t <[length]>); + +DESCRIPTION + This function converts the argument <[c]> into an unsigned + char and fills the first <[length]> characters of the array + pointed to by <[dst]> to the value. + +RETURNS + <<memset>> returns the value of <[dst]>. + +PORTABILITY +<<memset>> is ANSI C. + + <<memset>> requires no supporting OS subroutines. + +QUICKREF + memset ansi pure +*/ + +#include <string.h> +#include "local.h" + +#define LBLOCKSIZE (sizeof(long)) +#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +void * +__inhibit_loop_to_libcall +memset (void *m, + int c, + size_t n) +{ + char *s = (char *) m; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned int i; + unsigned long buffer; + unsigned long *aligned_addr; + unsigned int d = c & 0xff; /* To avoid sign extension, copy C to an + unsigned variable. */ + + while (UNALIGNED (s)) + { + if (n--) + *s++ = (char) c; + else + return m; + } + + if (!TOO_SMALL (n)) + { + /* If we get this far, we know that n is large and s is word-aligned. */ + aligned_addr = (unsigned long *) s; + + /* Store D into each char sized location in BUFFER so that + we can set large blocks quickly. */ + buffer = (d << 8) | d; + buffer |= (buffer << 16); + for (i = 32; i < LBLOCKSIZE * 8; i <<= 1) + buffer = (buffer << i) | buffer; + + /* Unroll the loop. */ + while (n >= LBLOCKSIZE*4) + { + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + n -= 4*LBLOCKSIZE; + } + + while (n >= LBLOCKSIZE) + { + *aligned_addr++ = buffer; + n -= LBLOCKSIZE; + } + /* Pick up the remainder with a bytewise loop. */ + s = (char*)aligned_addr; + } + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (n--) + *s++ = (char) c; + + return m; +} diff --git a/src/libc/readme-libc.txt b/src/libc/readme-libc.txt new file mode 100644 index 00000000..40c60f62 --- /dev/null +++ b/src/libc/readme-libc.txt @@ -0,0 +1,8 @@ +README file for libc folder +=========================== + +On ARM Cortex M7 processors, accesses to strongly-ordered memory such as our "nocache" RAM segment must be aligned. Therefore we compile with option -fno-unaligned-access. +Unfortunately, newlib (the standard C library) isn't compiled with -fno-unaligned-access, and memcpy in particular sometimes does unaligned accesses. +To fix this, we include our own copy of memcpy here. Similarly for other memory-related functions that might be used to access DMA buffers. + +DC 2019-12-24 |