You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
352 lines
9.9 KiB
352 lines
9.9 KiB
/* |
|
* Copyright (c) 2017 Imagination Technologies. |
|
* |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* |
|
* * Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* * Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer |
|
* in the documentation and/or other materials provided with |
|
* the distribution. |
|
* * Neither the name of Imagination Technologies nor the names of its |
|
* contributors may be used to endorse or promote products derived |
|
* from this software without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
|
|
#include <string.h> |
|
#include <stdint.h> |
|
|
|
#define ENABLE_PREFETCH 1 |
|
|
|
#define STRNG(X) #X |
|
#define PREFETCH(src_ptr, offset) \ |
|
asm("pref 0, " STRNG(offset) "(%[src]) \n\t" : : [src] "r" (src_ptr)); |
|
|
|
#if !defined(UNALIGNED_INSTR_SUPPORT) |
|
/* does target have unaligned lw/ld/ualw/uald instructions? */ |
|
#define UNALIGNED_INSTR_SUPPORT 0 |
|
#if __mips_isa_rev < 6 && !__mips1 |
|
#undef UNALIGNED_INSTR_SUPPORT |
|
#define UNALIGNED_INSTR_SUPPORT 1 |
|
#endif |
|
#endif |
|
|
|
#if !defined(HW_UNALIGNED_SUPPORT) |
|
/* Does target have hardware support for unaligned accesses? */ |
|
#define HW_UNALIGNED_SUPPORT 0 |
|
#if __mips_isa_rev >= 6 |
|
#undef HW_UNALIGNED_SUPPORT |
|
#define HW_UNALIGNED_SUPPORT 1 |
|
#endif |
|
#endif |
|
|
|
#define SIZEOF_reg_t 4 |
|
#if _MIPS_SIM == _ABIO32 |
|
typedef unsigned long reg_t; |
|
typedef struct bits |
|
{ |
|
reg_t B0:8, B1:8, B2:8, B3:8; |
|
} bits_t; |
|
#else |
|
#undef SIZEOF_reg_t |
|
#define SIZEOF_reg_t 8 |
|
typedef unsigned long long reg_t; |
|
typedef struct bits |
|
{ |
|
reg_t B0:8, B1:8, B2:8, B3:8, B4:8, B5:8, B6:8, B7:8; |
|
} bits_t; |
|
#endif |
|
|
|
/* This union assumes that small structures can be in registers. If |
|
not, then memory accesses will be done - not optimal, but ok. */ |
|
typedef union |
|
{ |
|
reg_t v; |
|
bits_t b; |
|
} bitfields_t; |
|
|
|
#define do_bitfield(__i) \ |
|
if (x.b.B##__i != y.b.B##__i) return x.b.B##__i - y.b.B##__i; |
|
|
|
/* pull apart the words to find the first differing unsigned byte. */ |
|
static int __attribute__ ((noinline)) do_by_bitfields (reg_t a, reg_t b) |
|
{ |
|
bitfields_t x, y; |
|
x.v = a; |
|
y.v = b; |
|
do_bitfield (0); |
|
do_bitfield (1); |
|
do_bitfield (2); |
|
#if SIZEOF_reg_t == 4 |
|
return x.b.B3 - y.b.B3; |
|
#else |
|
do_bitfield (3); |
|
do_bitfield (4); |
|
do_bitfield (5); |
|
do_bitfield (6); |
|
return x.b.B7 - y.b.B7; |
|
#endif |
|
} |
|
|
|
/* This code is called when aligning a pointer, there are remaining bytes |
|
after doing word compares, or architecture does not have some form |
|
of unaligned support. */ |
|
static inline int __attribute__ ((always_inline)) |
|
do_bytes (const void *a, const void *b, unsigned long len) |
|
{ |
|
unsigned char *x = (unsigned char *) a; |
|
unsigned char *y = (unsigned char *) b; |
|
unsigned long i; |
|
|
|
/* 'len' might be zero here, so preloading the first two values |
|
before the loop may access unallocated memory. */ |
|
for (i = 0; i < len; i++) { |
|
if (*x != *y) |
|
return *x - *y; |
|
x++; |
|
y++; |
|
} |
|
return 0; |
|
} |
|
|
|
#if !HW_UNALIGNED_SUPPORT |
|
#if UNALIGNED_INSTR_SUPPORT |
|
/* for MIPS GCC, there are no unaligned builtins - so this struct forces |
|
the compiler to treat the pointer access as unaligned. */ |
|
struct ulw |
|
{ |
|
reg_t uli; |
|
} __attribute__ ((packed)); |
|
|
|
/* first pointer is not aligned while second pointer is. */ |
|
static int unaligned_words (const struct ulw *a, const reg_t *b, |
|
unsigned long words, unsigned long bytes) |
|
{ |
|
#if ENABLE_PREFETCH |
|
/* prefetch pointer aligned to 32 byte boundary */ |
|
const reg_t *pref_ptr = (const reg_t *) (((uintptr_t) b + 31) & ~31); |
|
const reg_t *pref_ptr_a = (const reg_t *) (((uintptr_t) a + 31) & ~31); |
|
#endif |
|
for (; words >= 16; words -= 8) { |
|
#if ENABLE_PREFETCH |
|
pref_ptr += 8; |
|
PREFETCH(pref_ptr, 0); |
|
PREFETCH(pref_ptr, 32); |
|
|
|
pref_ptr_a += 8; |
|
PREFETCH(pref_ptr_a, 0); |
|
PREFETCH(pref_ptr_a, 32); |
|
#endif |
|
reg_t x0 = a[0].uli, x1 = a[1].uli; |
|
reg_t x2 = a[2].uli, x3 = a[3].uli; |
|
reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3]; |
|
if (x0 != y0) |
|
return do_by_bitfields (x0, y0); |
|
if (x1 != y1) |
|
return do_by_bitfields (x1, y1); |
|
if (x2 != y2) |
|
return do_by_bitfields (x2, y2); |
|
if (x3 != y3) |
|
return do_by_bitfields (x3, y3); |
|
|
|
x0 = a[4].uli; x1 = a[5].uli; |
|
x2 = a[6].uli; x3 = a[7].uli; |
|
y0 = b[4]; y1 = b[5]; y2 = b[6]; y3 = b[7]; |
|
if (x0 != y0) |
|
return do_by_bitfields (x0, y0); |
|
if (x1 != y1) |
|
return do_by_bitfields (x1, y1); |
|
if (x2 != y2) |
|
return do_by_bitfields (x2, y2); |
|
if (x3 != y3) |
|
return do_by_bitfields (x3, y3); |
|
|
|
a += 8; |
|
b += 8; |
|
} |
|
|
|
for (; words >= 4; words -= 4) { |
|
reg_t x0 = a[0].uli, x1 = a[1].uli; |
|
reg_t x2 = a[2].uli, x3 = a[3].uli; |
|
reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3]; |
|
if (x0 != y0) |
|
return do_by_bitfields (x0, y0); |
|
if (x1 != y1) |
|
return do_by_bitfields (x1, y1); |
|
if (x2 != y2) |
|
return do_by_bitfields (x2, y2); |
|
if (x3 != y3) |
|
return do_by_bitfields (x3, y3); |
|
a += 4; |
|
b += 4; |
|
} |
|
|
|
/* do remaining words. */ |
|
while (words--) { |
|
reg_t x0 = a->uli; |
|
reg_t y0 = *b; |
|
a += 1; |
|
b += 1; |
|
if (x0 != y0) |
|
return do_by_bitfields (x0, y0); |
|
} |
|
|
|
/* mop up any remaining bytes. */ |
|
return do_bytes (a, b, bytes); |
|
} |
|
#else |
|
/* no HW support or unaligned lw/ld/ualw/uald instructions. */ |
|
static int unaligned_words (const reg_t *a, const reg_t *b, |
|
unsigned long words, unsigned long bytes) |
|
{ |
|
return do_bytes (a, b, (sizeof (reg_t) * words) + bytes); |
|
} |
|
#endif /* UNALIGNED_INSTR_SUPPORT */ |
|
#endif /* HW_UNALIGNED_SUPPORT */ |
|
|
|
/* both pointers are aligned, or first isn't and HW support for unaligned. */ |
|
static int aligned_words (const reg_t *a, const reg_t *b, |
|
unsigned long words, unsigned long bytes) |
|
{ |
|
#if ENABLE_PREFETCH |
|
/* prefetch pointer aligned to 32 byte boundary */ |
|
const reg_t *pref_ptr = (const reg_t *) (((uintptr_t) b + 31) & ~31); |
|
const reg_t *pref_ptr_a = (const reg_t *) (((uintptr_t) a + 31) & ~31); |
|
#endif |
|
|
|
for (; words >= 24; words -= 12) { |
|
#if ENABLE_PREFETCH |
|
pref_ptr += 12; |
|
PREFETCH(pref_ptr, 0); |
|
PREFETCH(pref_ptr, 32); |
|
PREFETCH(pref_ptr, 64); |
|
|
|
pref_ptr_a += 12; |
|
PREFETCH(pref_ptr_a, 0); |
|
PREFETCH(pref_ptr_a, 32); |
|
PREFETCH(pref_ptr_a, 64); |
|
#endif |
|
reg_t x0 = a[0], x1 = a[1], x2 = a[2], x3 = a[3]; |
|
reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3]; |
|
if (x0 != y0) |
|
return do_by_bitfields (x0, y0); |
|
if (x1 != y1) |
|
return do_by_bitfields (x1, y1); |
|
if (x2 != y2) |
|
return do_by_bitfields (x2, y2); |
|
if (x3 != y3) |
|
return do_by_bitfields (x3, y3); |
|
|
|
x0 = a[4]; x1 = a[5]; x2 = a[6]; x3 = a[7]; |
|
y0 = b[4]; y1 = b[5]; y2 = b[6]; y3 = b[7]; |
|
if (x0 != y0) |
|
return do_by_bitfields (x0, y0); |
|
if (x1 != y1) |
|
return do_by_bitfields (x1, y1); |
|
if (x2 != y2) |
|
return do_by_bitfields (x2, y2); |
|
if (x3 != y3) |
|
return do_by_bitfields (x3, y3); |
|
|
|
x0 = a[8]; x1 = a[9]; x2 = a[10]; x3 = a[11]; |
|
y0 = b[8]; y1 = b[9]; y2 = b[10]; y3 = b[11]; |
|
if (x0 != y0) |
|
return do_by_bitfields (x0, y0); |
|
if (x1 != y1) |
|
return do_by_bitfields (x1, y1); |
|
if (x2 != y2) |
|
return do_by_bitfields (x2, y2); |
|
if (x3 != y3) |
|
return do_by_bitfields (x3, y3); |
|
|
|
a += 12; |
|
b += 12; |
|
} |
|
|
|
for (; words >= 4; words -= 4) { |
|
reg_t x0 = a[0], x1 = a[1], x2 = a[2], x3 = a[3]; |
|
reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3]; |
|
if (x0 != y0) |
|
return do_by_bitfields (x0, y0); |
|
if (x1 != y1) |
|
return do_by_bitfields (x1, y1); |
|
if (x2 != y2) |
|
return do_by_bitfields (x2, y2); |
|
if (x3 != y3) |
|
return do_by_bitfields (x3, y3); |
|
a += 4; |
|
b += 4; |
|
} |
|
|
|
/* do remaining words. */ |
|
while (words--) { |
|
reg_t x0 = *a; |
|
reg_t y0 = *b; |
|
a += 1; |
|
b += 1; |
|
if (x0 != y0) |
|
return do_by_bitfields (x0, y0); |
|
} |
|
|
|
/* mop up any remaining bytes. */ |
|
return do_bytes (a, b, bytes); |
|
} |
|
|
|
int memcmp (const void *a, const void *b, size_t len) |
|
{ |
|
unsigned long bytes, words; |
|
|
|
/* shouldn't hit that often. */ |
|
if (len < sizeof (reg_t) * 4) { |
|
return do_bytes (a, b, len); |
|
} |
|
|
|
/* Align the second pointer to word/dword alignment. |
|
Note that the pointer is only 32-bits for o32/n32 ABIs. For |
|
n32, loads are done as 64-bit while address remains 32-bit. */ |
|
bytes = ((unsigned long) b) % sizeof (reg_t); |
|
if (bytes) { |
|
int res; |
|
bytes = sizeof (reg_t) - bytes; |
|
if (bytes > len) |
|
bytes = len; |
|
res = do_bytes (a, b, bytes); |
|
if (res || len == bytes) |
|
return res; |
|
len -= bytes; |
|
a = (const void *) (((unsigned char *) a) + bytes); |
|
b = (const void *) (((unsigned char *) b) + bytes); |
|
} |
|
|
|
/* Second pointer now aligned. */ |
|
words = len / sizeof (reg_t); |
|
bytes = len % sizeof (reg_t); |
|
|
|
#if HW_UNALIGNED_SUPPORT |
|
/* treat possible unaligned first pointer as aligned. */ |
|
return aligned_words (a, b, words, bytes); |
|
#else |
|
if (((unsigned long) a) % sizeof (reg_t) == 0) { |
|
return aligned_words (a, b, words, bytes); |
|
} |
|
/* need to use unaligned instructions on first pointer. */ |
|
return unaligned_words (a, b, words, bytes); |
|
#endif |
|
}
|
|
|