Mercurial > vec
changeset 15:e05c257c6a23
*: huge refactor, add many new x86 intrinsics and the like
ALSO!! now intrinsics are enabled at runtime, depending on what is
detected. altivec *should* still work but I only tested compiling
it.
the major version has been updated to 2.0 for this...
author | Paper <paper@tflc.us> |
---|---|
date | Wed, 20 Nov 2024 04:10:37 -0500 |
parents | 981cf0bc7f3a |
children | 9da2aba90c87 |
files | CMakeLists.txt README include/vec/impl/altivec.h include/vec/impl/cpu.h include/vec/impl/fallback.h include/vec/impl/gcc.h include/vec/impl/generic.h include/vec/impl/ppc/altivec.h include/vec/impl/sse2.h include/vec/impl/x86/avx2.h include/vec/impl/x86/avx512f.h include/vec/impl/x86/mmx.h include/vec/impl/x86/sse2.h include/vec/impl/x86/sse41.h include/vec/vec.h src/vec.c test/Makefile test/Makefile.ppc test/Makefile.template test/Makefile.x86 test/main.c test/test.c test/test_align.h test/test_arith.h test/test_compare.h |
diffstat | 25 files changed, 3008 insertions(+), 2360 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Tue Nov 19 15:55:01 2024 -0500 +++ b/CMakeLists.txt Wed Nov 20 04:10:37 2024 -0500 @@ -1,10 +1,42 @@ cmake_minimum_required(VERSION 3.5) -project(vec VERSION 1.0.0 DESCRIPTION "a tiny C99 SIMD vector library") +project(vec VERSION 2.0.0 DESCRIPTION "a tiny C99 SIMD vector library") add_library(vec SHARED src/vec.c) -set_target_properties(vec PROPERTIES PUBLIC_HEADER include/vec/vec.h) +include(CheckCCompilerFlag) + +if(MSVC) + # TODO ? +else() + check_c_compiler_flag("-maltivec" COMPILER_HAS_ALTIVEC) + if(COMPILER_HAS_ALTIVEC) + target_compile_options(vec PRIVATE "-maltivec") + endif() + check_c_compiler_flag("-mmmx" COMPILER_HAS_MMX) + if(COMPILER_HAS_MMX) + target_compile_options(vec PRIVATE "-mmmx") + endif() + check_c_compiler_flag("-msse2" COMPILER_HAS_SSE2) + if(COMPILER_HAS_SSE2) + target_compile_options(vec PRIVATE "-msse2") + endif() + check_c_compiler_flag("-msse4.1" COMPILER_HAS_SSE41) + if(COMPILER_HAS_SSE41) + target_compile_options(vec PRIVATE "-msse4.1") + endif() + check_c_compiler_flag("-mavx2" COMPILER_HAS_AVX2) + if(COMPILER_HAS_AVX2) + target_compile_options(vec PRIVATE "-mavx2") + endif() + check_c_compiler_flag("-mavx512f" COMPILER_HAS_AVX512F) + if(COMPILER_HAS_AVX512F) + target_compile_options(vec PRIVATE "-mavx512f") + endif() +endif() + + +set_target_properties(vec PROPERTIES PUBLIC_HEADER include/vec/vec.h C_STANDARD 99) target_include_directories(vec PRIVATE include) @@ -13,9 +45,9 @@ include(GNUInstallDirs) install(TARGETS vec - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) # pkg-config configure_file(vec.pc.in vec.pc @ONLY) -install(FILES ${CMAKE_BINARY_DIR}/vec.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) \ No newline at end of file +install(FILES ${CMAKE_BINARY_DIR}/vec.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
--- a/README Tue Nov 19 15:55:01 2024 -0500 +++ b/README Wed Nov 20 04:10:37 2024 -0500 @@ -1,19 +1,19 @@ vec - a tiny SIMD vector header-only library written in C99 -it comes with an extremely basic (and somewhat lacking) API, -where there are eight supported vector types, all 128-bit: +it comes with an extremely basic API that is similar to other intrinsics +libraries; each type is in the exact same format: - vint8x16 - 16 signed 8-bit integers - vint16x8 - 8 signed 16-bit integers - vint32x4 - 4 signed 32-bit integers - vint64x2 - 2 signed 64-bit integers - vuint8x16 - 16 unsigned 8-bit integers - vuint16x8 - 8 unsigned 16-bit integers - vuint32x4 - 4 unsigned 32-bit integers - vuint32x4 - 2 unsigned 64-bit integers + v[sign][bits]x[size] + where `sign' is either nothing (for signed) or `u' (for unsigned), + `bits' is the bit size of the integer format, + and `size' is the how many integers are in the vector -all of these have many operations that are prefixed with the -name of the type and an underscore, for example: +vec provides types for 64-bit, 128-bit, 256-bit, and 512-bit SIMD intrinsics +on processors where vec has an implementation and falls back to array-based +implementations where they are not. + +all of these have many operations that are prefixed with the name of the +type and an underscore, for example: vint8x16 vint8x16_splat(uint8_t x) - creates a vint8x16 where all of the values are filled @@ -106,3 +106,10 @@ the result vector if the value in `vec1' is greater than or equal to the corresponding value in `vec2', else all of the bits are turned off. + +to initialize vec, you MUST call `vec_init()' when your programs starts up. + +note that `vec_init()' is NOT thread-safe, and things can and will +blow up if you call it simultaneously from different threads (i.e. you +try to only initialize it when you need to... please just initialize +it on startup so you don't have to worry about that!!!)
--- a/include/vec/impl/altivec.h Tue Nov 19 15:55:01 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,219 +0,0 @@ -/** - * vec - a tiny SIMD vector library in plain C99 - * - * Copyright (c) 2024 Paper - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -**/ - -/* Altivec vector support. */ - -#include <stdint.h> -#include <string.h> - -#include <altivec.h> - -#define VEC_ALTIVEC_ALIGNMENT 16 - -/* GCC 4.2.1 on Mac OS X doesn't have these for some reason */ -#ifdef vec_mul -# define VEC_ALTIVEC_MUL(sign, csign, bits, size) \ - VEC_DECL_MUL(sign, csign, bits, size) \ - { \ - return vec_mul(vec1, vec2); \ - } -#else -# define VEC_ALTIVEC_MUL(sign, csign, bits, size) \ - VEC_GENERIC_MULTIPLY(sign, csign, bits, size) -#endif - -#ifdef vec_splats -# define VEC_ALTIVEC_SPLAT(sign, csign, bits, size) \ - VEC_DECL_SPLAT(sign, bits, size) \ - { \ - return vec_splats(x); \ - } -#else -# define VEC_ALTIVEC_SPLAT(sign, csign, bits, size) \ - VEC_GENERIC_SPLAT(sign, csign, bits, size) -#endif - -#define VEC_ALTIVEC_uRSHIFT vec_sr -#define VEC_ALTIVEC_RSHIFT vec_sra - -#define VEC_ALTIVEC_uLRSHIFT(sign, csign, bits, size) \ - VEC_DECL_SHIFT(sign, bits, size, l, r) \ - { \ - return vec_sr(vec1, vec2); \ - } -#define VEC_ALTIVEC_LRSHIFT(sign, csign, bits, size) \ - VEC_GENERIC_SHIFT(sign, csign, bits, size, l, r) - -/* Since altivec conveniently made their API super user friendly, we can just use - * one giant macro to define literally everything */ -#define VEC_DEFINE_OPERATIONS(sign, csign, bits, size) \ - VEC_DECL_LOAD_ALIGNED(sign, bits, size) \ - { \ - return vec_ld(0, in); \ - } \ - \ - VEC_DECL_LOAD(sign, bits, size) \ - { \ - return vec_perm(vec_ld(0, in), vec_ld(VEC_ALTIVEC_ALIGNMENT, in), vec_lvsl(0, in)); \ - } \ - \ - VEC_DECL_STORE_ALIGNED(sign, bits, size) \ - { \ - vec_st(vec, 0, out); \ - } \ - \ - VEC_DECL_STORE(sign, bits, size) \ - { \ - VEC_ALIGNED_ARRAY(sign##int##bits##_t, aligned_out, size, VEC_ALTIVEC_ALIGNMENT); \ - vec_st(vec, 0, aligned_out); \ - memcpy(out, aligned_out, size * sizeof(*aligned_out)); \ - } \ - \ - VEC_DECL_ADD(sign, bits, size) \ - { \ - return vec_add(vec1, vec2); \ - } \ - \ - VEC_DECL_SUB(sign, bits, size) \ - { \ - return vec_sub(vec1, vec2); \ - } \ - \ - VEC_ALTIVEC_MUL(sign, csign, bits, size) \ - \ - VEC_DECL_SHIFT(sign, bits, size, , l) \ - { \ - return vec_sl(vec1, vec2); \ - } \ - \ - VEC_DECL_SHIFT(sign, bits, size, , r) \ - { \ - return VEC_ALTIVEC_##sign##RSHIFT(vec1, vec2); \ - } \ - \ - VEC_ALTIVEC_##sign##LRSHIFT(sign, csign, bits, size) \ - \ - VEC_DECL_AVG(sign, bits, size) \ - { \ - return vec_avg(vec1, vec2); \ - } \ - \ - VEC_DECL_AND(sign, bits, size) \ - { \ - return vec_and(vec1, vec2); \ - } \ - \ - VEC_DECL_OR(sign, bits, size) \ - { \ - return vec_or(vec1, vec2); \ - } \ - \ - VEC_DECL_XOR(sign, bits, size) \ - { \ - return vec_xor(vec1, vec2); \ - } \ - \ - VEC_GENERIC_COMPARISONS(sign, csign, bits, size) \ - VEC_GENERIC_DIVIDE(sign, csign, bits, size) \ - VEC_ALTIVEC_SPLAT(sign, csign, bits, size) - -#ifndef VEC_VUINT8X16 -# define VEC_VUINT8X16 -typedef vector unsigned char vuint8x16; -# define VUINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - (vuint8x16){ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p } -# define VUINT8x16_ALIGNMENT VEC_ALTIVEC_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 8, 16) -#endif /* VEC_VUINT8X16 */ - -#ifndef VEC_VINT8X16 -# define VEC_VINT8X16 -typedef vector signed char vint8x16; -# define VINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - (vint8x16){ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p } -# define VINT8x16_ALIGNMENT VEC_ALTIVEC_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 8, 16) -#endif /* VEC_VINT8X16 */ - -#ifndef VEC_VUINT16X8 -# define VEC_VUINT16X8 -typedef vector unsigned short vuint16x8; -# define VUINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \ - (vuint16x8){ a, b, c, d, e, f, g, h } -# define VUINT16x8_ALIGNMENT VEC_ALTIVEC_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 16, 8) -#endif /* VEC_VUINT16X8 */ - -#ifndef VEC_VINT16X8 -# define VEC_VINT16X8 -typedef vector signed short vint16x8; -# define VINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \ - (vint16x8){ a, b, c, d, e, f, g, h } -# define VINT16x8_ALIGNMENT VEC_ALTIVEC_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 16, 8) -#endif /* VEC_VINT16X8 */ - -#ifndef VEC_VUINT32X4 -# define VEC_VUINT32X4 -typedef vector unsigned int vuint32x4; -# define VUINT32x4_CONSTANT(a, b, c, d) \ - (vuint32x4){ a, b, c, d } -# define VUINT32x4_ALIGNMENT VEC_ALTIVEC_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 32, 4) -#endif /* VEC_VUINT32X4 */ - -#ifndef VEC_VINT32X4 -# define VEC_VINT32X4 -typedef vector signed int vint32x4; -# define VINT32x4_CONSTANT(a, b, c, d) \ - (vint32x4){ a, b, c, d } -# define VINT32x4_ALIGNMENT VEC_ALTIVEC_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 32, 4) -#endif /* VEC_VINT32X4 */ - -#if defined(__POWER8__) && defined(__VSX__) - -# ifndef VEC_VUINT64X2 -# define VEC_VUINT64X2 -typedef vector unsigned long long vuint64x2; -# define VUINT64x2_CONSTANT(a, b) \ - (vuint64x2){ a, b } -# define VUINT64x2_ALIGNMENT VEC_ALTIVEC_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 64, 2) -# endif /* VEC_VUINT64X2 */ - -# ifndef VEC_VINT64X2 -# define VEC_VINT64X2 -typedef vector signed long long vint64x2; -# define VINT64x2_CONSTANT(a, b) \ - (vint64x2){ a, b } -# define VINT64x2_ALIGNMENT VEC_ALTIVEC_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 64, 2) -# endif /* VEC_VINT64X2 */ - -#endif /* defined(__POWER8__) && defined(__VSX__) */ - -#undef VEC_DEFINE_OPERATIONS -#undef VEC_ALTIVEC_MUL -#undef VEC_ALTIVEC_SPLAT
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/cpu.h Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,397 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_CPU_H_ +#define VEC_IMPL_CPU_H_ + +/* Detect CPU SIMD support. Much of this code was stolen from SDL. + * + * Simple DirectMedia Layer + * Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + +# if defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__)) +# include <sys/sysctl.h> // For AltiVec check +# elif defined(__OpenBSD__) && defined(__powerpc__) +# include <sys/types.h> +# include <sys/sysctl.h> // For AltiVec check +# include <machine/cpu.h> +# elif defined(__FreeBSD__) && defined(__powerpc__) +# include <machine/cpu.h> +# include <sys/auxv.h> +# elif defined(__ALTIVEC__) +# include <signal.h> +# include <setjmp.h> +# endif + +# ifdef __FreeBSD__ +# include <sys/param.h> +# endif + +static inline int vec_CPU_have_CPUID(void) +{ + int has_CPUID = 0; + +#if (defined(__GNUC__) || defined(__llvm__)) && defined(__i386__) + __asm__ ( +" pushfl # Get original EFLAGS \n" +" popl %%eax \n" +" movl %%eax,%%ecx \n" +" xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n" +" pushl %%eax # Save new EFLAGS value on stack \n" +" popfl # Replace current EFLAGS value \n" +" pushfl # Get new EFLAGS \n" +" popl %%eax # Store new EFLAGS in EAX \n" +" xorl %%ecx,%%eax # Can not toggle ID bit, \n" +" jz 1f # Processor=80486 \n" +" movl $1,%0 # We have CPUID support \n" +"1: \n" + : "=m" (has_CPUID) + : + : "%eax", "%ecx" + ); +#elif (defined(__GNUC__) || defined(__llvm__)) && defined(__x86_64__) +/* Technically, if this is being compiled under __x86_64__ then it has + CPUid by definition. But it's nice to be able to prove it. :) */ + __asm__ ( +" pushfq # Get original EFLAGS \n" +" popq %%rax \n" +" movq %%rax,%%rcx \n" +" xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n" +" pushq %%rax # Save new EFLAGS value on stack \n" +" popfq # Replace current EFLAGS value \n" +" pushfq # Get new EFLAGS \n" +" popq %%rax # Store new EFLAGS in EAX \n" +" xorl %%ecx,%%eax # Can not toggle ID bit, \n" +" jz 1f # Processor=80486 \n" +" movl $1,%0 # We have CPUID support \n" +"1: \n" + : "=m" (has_CPUID) + : + : "%rax", "%rcx" + ); +#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + __asm { + pushfd ; Get original EFLAGS + pop eax + mov ecx, eax + xor eax, 200000h ; Flip ID bit in EFLAGS + push eax ; Save new EFLAGS value on stack + popfd ; Replace current EFLAGS value + pushfd ; Get new EFLAGS + pop eax ; Store new EFLAGS in EAX + xor eax, ecx ; Can not toggle ID bit, + jz done ; Processor=80486 + mov has_CPUID,1 ; We have CPUID support +done: + } +#elif defined(_MSC_VER) && defined(_M_X64) + has_CPUID = 1; +#elif defined(__sun) && defined(__i386) + __asm ( +" pushfl \n" +" popl %eax \n" +" movl %eax,%ecx \n" +" xorl $0x200000,%eax \n" +" pushl %eax \n" +" popfl \n" +" pushfl \n" +" popl %eax \n" +" xorl %ecx,%eax \n" +" jz 1f \n" +" movl $1,-8(%ebp) \n" +"1: \n" + ); +#elif defined(__sun) && defined(__amd64) + __asm ( +" pushfq \n" +" popq %rax \n" +" movq %rax,%rcx \n" +" xorl $0x200000,%eax \n" +" pushq %rax \n" +" popfq \n" +" pushfq \n" +" popq %rax \n" +" xorl %ecx,%eax \n" +" jz 1f \n" +" movl $1,-8(%rbp) \n" +"1: \n" + ); +#endif + + return has_CPUID; +} + +#if (defined(__GNUC__) || defined(__llvm__)) && defined(__i386__) +# define VEC_CPU_CPUID(func, a, b, c, d) \ + __asm__ __volatile__( \ + " pushl %%ebx \n" \ + " xorl %%ecx,%%ecx \n" \ + " cpuid \n" \ + " movl %%ebx, %%esi \n" \ + " popl %%ebx \n" \ + : "=a"(a), "=S"(b), "=c"(c), "=d"(d) \ + : "a"(func)) +#elif (defined(__GNUC__) || defined(__llvm__)) && defined(__x86_64__) +# define VEC_CPU_CPUID(func, a, b, c, d) \ + __asm__ __volatile__( \ + " pushq %%rbx \n" \ + " xorq %%rcx,%%rcx \n" \ + " cpuid \n" \ + " movq %%rbx, %%rsi \n" \ + " popq %%rbx \n" \ + : "=a"(a), "=S"(b), "=c"(c), "=d"(d) \ + : "a"(func)) +#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) +# define VEC_CPU_CPUID(func, a, b, c, d) \ + __asm { \ + __asm mov eax, func \ + __asm xor ecx, ecx \ + __asm cpuid \ + __asm mov a, eax \ + __asm mov b, ebx \ + __asm mov c, ecx \ + __asm mov d, edx \ + } +#elif (defined(_MSC_VER) && defined(_M_X64)) +// Use __cpuidex instead of __cpuid because ICL does not clear ecx register +# define VEC_CPU_CPUID(func, a, b, c, d) \ + do { \ + int CPUInfo[4]; \ + __cpuidex(CPUInfo, func, 0); \ + a = CPUInfo[0]; \ + b = CPUInfo[1]; \ + c = CPUInfo[2]; \ + d = CPUInfo[3]; \ + } while (0) +#else +# define VEC_CPU_CPUID(func, a, b, c, d) \ + do { \ + a = b = c = d = 0; \ + (void)a; \ + (void)b; \ + (void)c; \ + (void)d; \ + } while (0) +#endif + +// --------------------------------------------------------------- + +static int vec_CPU_CPUIDFeatures[4]; +static int vec_CPU_CPUIDMaxFunction = 0; +static int vec_CPU_OSSavesYMM = 0; +static int vec_CPU_OSSavesZMM = 0; + +static inline void vec_CPU_get_CPUID_features(void) +{ + static int checked = 0; + if (!checked) { + checked = 1; + if (vec_CPU_have_CPUID()) { + int a, b, c, d; + VEC_CPU_CPUID(0, a, b, c, d); + vec_CPU_CPUIDMaxFunction = a; + if (vec_CPU_CPUIDMaxFunction >= 1) { + VEC_CPU_CPUID(1, a, b, c, d); + vec_CPU_CPUIDFeatures[0] = a; + vec_CPU_CPUIDFeatures[1] = b; + vec_CPU_CPUIDFeatures[2] = c; + vec_CPU_CPUIDFeatures[3] = d; + + // Check to make sure we can call xgetbv + if (c & 0x08000000) { + // Call xgetbv to see if YMM (etc) register state is saved +#if (defined(__GNUC__) || defined(__llvm__)) && (defined(__i386__) || defined(__x86_64__)) + __asm__(".byte 0x0f, 0x01, 0xd0" + : "=a"(a) + : "c"(0) + : "%edx"); +#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) && (_MSC_FULL_VER >= 160040219) // VS2010 SP1 + a = (int)_xgetbv(0); +#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + __asm { + xor ecx, ecx + _asm _emit 0x0f _asm _emit 0x01 _asm _emit 0xd0 + mov a, eax + } +#endif + vec_CPU_OSSavesYMM = ((a & 6) == 6) ? 1 : 0; + vec_CPU_OSSavesZMM = (vec_CPU_OSSavesYMM && ((a & 0xe0) == 0xe0)) ? 1 : 0; + } + } + } + } +} + +#if !((defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))) || (defined(__OpenBSD__) && defined(__powerpc__))) && defined(VEC_COMPILER_HAS_ALTIVEC) && defined(__GNUC__) +static jmp_buf vec_jmpbuf; +static void vec_CPU_illegal_instruction(int sig) +{ + longjmp(vec_jmpbuf, 1); +} +#endif + +static int vec_CPU_have_ALTIVEC(void) +{ + volatile int altivec = 0; +#if (defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))) || (defined(__OpenBSD__) && defined(__powerpc__)) + int selectors[2] = { +# ifdef __OpenBSD__ + CTL_MACHDEP, CPU_ALTIVEC +# else + CTL_HW, HW_VECTORUNIT +# endif + }; + int hasVectorUnit = 0; + size_t length = sizeof(hasVectorUnit); + int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0); + if (!error) + altivec = (hasVectorUnit != 0); +#elif defined(__FreeBSD__) && defined(__powerpc__) + unsigned long cpufeatures = 0; + elf_aux_info(AT_HWCAP, &cpufeatures, sizeof(cpufeatures)); + altivec = cpufeatures & PPC_FEATURE_HAS_ALTIVEC; +#elif defined(VEC_COMPILER_HAS_ALTIVEC) && defined(__GNUC__) + void (*handler)(int sig); + handler = signal(SIGILL, vec_CPU_illegal_instruction); + if (!setjmp(vec_jmpbuf)) { + asm volatile("mtspr 256, %0\n\t" + "vand %%v0, %%v0, %%v0" ::"r"(-1)); + altivec = 1; + } + signal(SIGILL, handler); +#endif + return altivec; +} + +static int vec_CPU_have_ALTIVEC_VSX(void) +{ + volatile int vsx = 0; +#if defined(VEC_COMPILER_HAS_ALTIVEC_VSX) && defined(__GNUC__) + void (*handler)(int sig); + handler = signal(SIGILL, vec_CPU_illegal_instruction); + if (!setjmp(vec_jmpbuf)) { + // this is completely untested + asm volatile("mtspr 256, %0\n\t" + "xxland %%v0, %%v0, %%v0" ::"r"(-1)); + vsx = 1; + } + signal(SIGILL, handler); +#endif + return vsx; +} + +#define vec_CPU_have_MMX() (vec_CPU_CPUIDFeatures[3] & 0x00800000) +#define vec_CPU_have_SSE() (vec_CPU_CPUIDFeatures[3] & 0x02000000) +#define vec_CPU_have_SSE2() (vec_CPU_CPUIDFeatures[3] & 0x04000000) +#define vec_CPU_have_SSE3() (vec_CPU_CPUIDFeatures[2] & 0x00000001) +#define vec_CPU_have_SSE41() (vec_CPU_CPUIDFeatures[2] & 0x00080000) +#define vec_CPU_have_SSE42() (vec_CPU_CPUIDFeatures[2] & 0x00100000) +#define vec_CPU_have_AVX() (vec_CPU_OSSavesYMM && (vec_CPU_CPUIDFeatures[2] & 0x10000000)) + +static inline int vec_CPU_have_AVX2(void) +{ + if (vec_CPU_OSSavesYMM && (vec_CPU_CPUIDMaxFunction >= 7)) { + int a, b, c, d; + VEC_CPU_CPUID(7, a, b, c, d); + return b & 0x00000020; + (void)a, (void)c, (void)d; + } + return 0; +} + +static inline int vec_CPU_have_AVX512F(void) +{ + if (vec_CPU_OSSavesYMM && (vec_CPU_CPUIDMaxFunction >= 7)) { + int a, b, c, d; + VEC_CPU_CPUID(7, a, b, c, d); + return b & 0x00000020; + (void)a, (void)c, (void)d; + } + return 0; +} + +enum { + VEC_CPU_HAS_ALTIVEC = (1 << 0), + VEC_CPU_HAS_ALTIVEC_VSX = (1 << 1), + VEC_CPU_HAS_MMX = (1 << 2), + VEC_CPU_HAS_SSE = (1 << 3), + VEC_CPU_HAS_SSE2 = (1 << 4), + VEC_CPU_HAS_SSE3 = (1 << 5), + VEC_CPU_HAS_SSE41 = (1 << 6), + VEC_CPU_HAS_SSE42 = (1 << 7), + VEC_CPU_HAS_AVX = (1 << 8), + VEC_CPU_HAS_AVX2 = (1 << 9), + VEC_CPU_HAS_AVX512F = (1 << 10), +}; + +#define VEC_CPU_FEATURES_RESET UINT32_C(0xFFFFFFFF) + +static uint32_t vec_CPU_features = VEC_CPU_FEATURES_RESET; + +static void vec_get_CPU_features(void) +{ + vec_CPU_get_CPUID_features(); + vec_CPU_features = 0; + if (vec_CPU_have_ALTIVEC()) + vec_CPU_features |= VEC_CPU_HAS_ALTIVEC; + if (vec_CPU_have_ALTIVEC_VSX()) + vec_CPU_features |= VEC_CPU_HAS_ALTIVEC_VSX; + if (vec_CPU_have_MMX()) + vec_CPU_features |= VEC_CPU_HAS_MMX; + if (vec_CPU_have_SSE()) + vec_CPU_features |= VEC_CPU_HAS_SSE; + if (vec_CPU_have_SSE2()) + vec_CPU_features |= VEC_CPU_HAS_SSE2; + if (vec_CPU_have_SSE3()) + vec_CPU_features |= VEC_CPU_HAS_SSE3; + if (vec_CPU_have_SSE41()) + vec_CPU_features |= VEC_CPU_HAS_SSE41; + if (vec_CPU_have_SSE42()) + vec_CPU_features |= VEC_CPU_HAS_SSE42; + if (vec_CPU_have_AVX()) + vec_CPU_features |= VEC_CPU_HAS_AVX; + if (vec_CPU_have_AVX2()) + vec_CPU_features |= VEC_CPU_HAS_AVX2; + if (vec_CPU_have_AVX512F()) + vec_CPU_features |= VEC_CPU_HAS_AVX512F; +} + +#endif /* VEC_IMPL_CPU_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/fallback.h Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,202 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_FALLBACK_H_ +#define VEC_IMPL_FALLBACK_H_ + +// Fallback implementations - this is what an implementation should use if it +// doesn't support a specific function. Note that the load_aligned and +// store_aligned functions are not implemented here - this is on purpose; +// every single implementation *needs* to have one of these. + +#define VEC_FALLBACK_OPERATION(op, sign, csign, bits, size) \ + do { \ + V##csign##INT##bits##x##size##_ALIGNED_ARRAY(varr1); \ + V##csign##INT##bits##x##size##_ALIGNED_ARRAY(varr2); \ + \ + v##sign##int##bits##x##size##_store_aligned(vec1, varr1); \ + v##sign##int##bits##x##size##_store_aligned(vec2, varr2); \ + \ + for (int i = 0; i < size; i++) varr1[i] = (op); \ + \ + return v##sign##int##bits##x##size##_load_aligned(varr1); \ + } while (0) + +#define VEC_FALLBACK_CMP(op, sign, csign, bits, size) \ + VEC_FALLBACK_OPERATION((varr1[i] op varr2[i]) ? UINT##bits##_MAX : 0, sign, csign, bits, size) + +#define VEC_FALLBACK_SHIFT(op, sign, csign, bits, size) \ + do { \ + V##csign##INT##bits##x##size##_ALIGNED_ARRAY(varr1); \ + VUINT##bits##x##size##_ALIGNED_ARRAY(varr2); \ + \ + v##sign##int##bits##x##size##_store_aligned(vec1, varr1); \ + vuint##bits##x##size##_store_aligned(vec2, varr2); \ + \ + for (int i = 0; i < size; i++) varr1[i] = (op); \ + \ + return v##sign##int##bits##x##size##_load_aligned(varr1); \ + } while (0) + +#define VEC_DEFINE_FALLBACK_OPERATIONS_SIGN(sign, csign, bits, size) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_splat(sign##int##bits##_t x) \ + { \ + V##csign##INT##bits##x##size##_ALIGNED_ARRAY(arr); \ + for (int i = 0; i < size; i++) arr[i] = x; \ + return v##sign##int##bits##x##size##_load_aligned(arr); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_load(const sign##int##bits##_t in[size]) \ + { \ + V##csign##INT##bits##x##size##_ALIGNED_ARRAY(arr); \ + memcpy(arr, in, sizeof(sign##int##bits##_t) * size); \ + return v##sign##int##bits##x##size##_load_aligned(arr); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_store(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + V##csign##INT##bits##x##size##_ALIGNED_ARRAY(arr); \ + v##sign##int##bits##x##size##_store_aligned(vec, arr); \ + memcpy(out, arr, sizeof(sign##int##bits##_t) * size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_OPERATION(varr1[i] + varr2[i], sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_OPERATION(varr1[i] - varr2[i], sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_OPERATION(varr1[i] * varr2[i], sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_OPERATION(varr2[i] ? (varr1[i] / varr2[i]) : 0, sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return v##sign##int##bits##x##size##_div(v##sign##int##bits##x##size##_add(vec1, vec2), v##sign##int##bits##x##size##_splat(2)); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_OPERATION(varr1[i] & varr2[i], sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_OPERATION(varr1[i] | varr2[i], sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_OPERATION(varr1[i] ^ varr2[i], sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_not(v##sign##int##bits##x##size vec) \ + { \ + return v##sign##int##bits##x##size##_xor(vec, v##sign##int##bits##x##size##_splat((sign##int##bits##_t)UINT##bits##_MAX)); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_CMP(<, sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_CMP(<=, sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_CMP(==, sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_CMP(>=, sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_FALLBACK_CMP(>, sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_FALLBACK_SHIFT(vec_##sign##lshift(varr1[i], varr2[i]), sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_FALLBACK_SHIFT(vec_##sign##rshift(varr1[i], varr2[i]), sign, csign, bits, size); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_fallback_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_FALLBACK_SHIFT(vec_##sign##lrshift(varr1[i], varr2[i]), sign, csign, bits, size); \ + } + +#define VEC_DEFINE_FALLBACK_OPERATIONS(bits, size) \ + VEC_DEFINE_FALLBACK_OPERATIONS_SIGN( , , bits, size) \ + VEC_DEFINE_FALLBACK_OPERATIONS_SIGN(u, U, bits, size) + +// 64-bit +VEC_DEFINE_FALLBACK_OPERATIONS(8, 8) +VEC_DEFINE_FALLBACK_OPERATIONS(16, 4) +VEC_DEFINE_FALLBACK_OPERATIONS(32, 2) + +// 128-bit +VEC_DEFINE_FALLBACK_OPERATIONS(8, 16) +VEC_DEFINE_FALLBACK_OPERATIONS(16, 8) +VEC_DEFINE_FALLBACK_OPERATIONS(32, 4) +VEC_DEFINE_FALLBACK_OPERATIONS(64, 2) + +// 256-bit +VEC_DEFINE_FALLBACK_OPERATIONS(8, 32) +VEC_DEFINE_FALLBACK_OPERATIONS(16, 16) +VEC_DEFINE_FALLBACK_OPERATIONS(32, 8) +VEC_DEFINE_FALLBACK_OPERATIONS(64, 4) + +// 512-bit +VEC_DEFINE_FALLBACK_OPERATIONS(8, 64) +VEC_DEFINE_FALLBACK_OPERATIONS(16, 32) +VEC_DEFINE_FALLBACK_OPERATIONS(32, 16) +VEC_DEFINE_FALLBACK_OPERATIONS(64, 8) + +#undef VEC_FALLBACK_OPERATION +#undef VEC_FALLBACK_CMP +#undef VEC_FALLBACK_SHIFT +#undef VEC_DEFINE_FALLBACK_OPERATIONS +#undef VEC_DEFINE_FALLBACK_OPERATIONS_SIGN + +#endif /* VEC_IMPL_FALLBACK_H_ */
--- a/include/vec/impl/gcc.h Tue Nov 19 15:55:01 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,221 +0,0 @@ -/** - * vec - a tiny SIMD vector library in plain C99 - * - * Copyright (c) 2024 Paper - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -**/ - -/* GCC built in vectors */ - -#include <stdint.h> -#include <string.h> - -#define VEC_DEFINE_OPERATIONS(sign, csign, bits, size) \ - VEC_DECL_LOAD_ALIGNED(sign, bits, size) \ - { \ - v##sign##int##bits##x##size vec; \ - memcpy(&vec, in, sizeof(*in) * size); \ - return vec; \ - } \ - \ - VEC_DECL_LOAD(sign, bits, size) \ - { \ - return v##sign##int##bits##x##size##_load_aligned(in); \ - } \ - \ - VEC_DECL_STORE_ALIGNED(sign, bits, size) \ - { \ - memcpy(out, &vec, sizeof(vec)); \ - } \ - \ - VEC_DECL_STORE(sign, bits, size) \ - { \ - return v##sign##int##bits##x##size##_store_aligned(vec, out); \ - } \ - \ - VEC_DECL_ADD(sign, bits, size) \ - { \ - return vec1 + vec2; \ - } \ - \ - VEC_DECL_SUB(sign, bits, size) \ - { \ - return vec1 - vec2; \ - } \ - \ - VEC_DECL_MUL(sign, bits, size) \ - { \ - return vec1 * vec2; \ - } \ - \ - VEC_DECL_AND(sign, bits, size) \ - { \ - return vec1 & vec2; \ - } \ - \ - VEC_DECL_OR(sign, bits, size) \ - { \ - return vec1 | vec2; \ - } \ - \ - VEC_DECL_XOR(sign, bits, size) \ - { \ - return vec1 ^ vec2; \ - } \ - VEC_DECL_CMPLT(sign, bits, size) \ - { \ - return vec1 < vec2; \ - } \ - VEC_DECL_CMPGT(sign, bits, size) \ - { \ - return vec1 > vec2; \ - } \ - VEC_DECL_CMPEQ(sign, bits, size) \ - { \ - return vec1 == vec2; \ - } \ - VEC_DECL_CMPLE(sign, bits, size) \ - { \ - return vec1 <= vec2; \ - } \ - VEC_DECL_CMPGE(sign, bits, size) \ - { \ - return vec1 >= vec2; \ - } \ - \ - VEC_GENERIC_DIVIDE(sign, csign, bits, size) \ - VEC_GENERIC_SPLAT(sign, csign, bits, size) \ - VEC_GENERIC_SHIFTS(sign, csign, bits, size) \ - VEC_GENERIC_AVG(sign, bits, size) - -// ----------------------------------------------------------------------------------- -// 128-bit vector types - -#ifndef VEC_VUINT8X16 -# define VEC_VUINT8X16 -typedef uint8_t vuint8x16 __attribute__((__vector_size__(16))); -# define VUINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - (vuint8x16){ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p } -# define VUINT8x16_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(u, U, 8, 16) -#endif - -#ifndef VEC_VUINT16X8 -# define VEC_VUINT16X8 -typedef uint16_t vuint16x8 __attribute__((__vector_size__(16))); -# define VUINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \ - (vuint16x8){ a, b, c, d, e, f, g, h } -# define VUINT16x8_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(u, U, 16, 8) -#endif - -#ifndef VEC_VUINT32X4 -# define VEC_VUINT32X4 -typedef uint32_t vuint32x4 __attribute__((__vector_size__(16))); -# define VUINT32x4_CONSTANT(a, b, c, d) \ - (vuint32x4){ a, b, c, d } -# define VUINT32x4_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(u, U, 32, 4) -#endif - -#ifndef VEC_VUINT64X2 -# define VEC_VUINT64X2 -typedef uint64_t vuint64x2 __attribute__((__vector_size__(16))); -# define VUINT64x2_CONSTANT(a, b) \ - (vuint64x2){ a, b } -# define VUINT64x2_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(u, U, 64, 2) -#endif - -#ifndef VEC_VINT8X16 -# define VEC_VINT8X16 -typedef int8_t vint8x16 __attribute__((__vector_size__(16))); -# define VINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - (vint8x16){ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p } -# define VINT8x16_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(, , 8, 16) -#endif - -#ifndef VEC_VINT16X8 -# define VEC_VINT16X8 -typedef int16_t vint16x8 __attribute__((__vector_size__(16))); -# define VINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \ - (vint16x8){ a, b, c, d, e, f, g, h } -# define VINT16x8_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(, , 16, 8) -#endif - -#ifndef VEC_VINT32X4 -# define VEC_VINT32X4 -typedef int32_t vint32x4 __attribute__((__vector_size__(16))); -# define VINT32x4_CONSTANT(a, b, c, d) \ - (vint32x4){ a, b, c, d } -# define VINT32x4_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(, , 32, 4) -#endif - -#ifndef VEC_VINT64X2 -# define VEC_VINT64X2 -typedef int64_t vint64x2 __attribute__((__vector_size__(16))); -# define VINT64x2_CONSTANT(a, b) \ - (vint64x2){ a, b } -# define VINT64x2_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(, , 64, 2) -#endif - -#ifndef VEC_VUINT8X16 -# define VEC_VUINT8X16 -typedef uint8_t vuint8x16 __attribute__((__vector_size__(16))); -# define VUINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - (vuint8x16){ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p } -# define VUINT8x16_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(u, U, 8, 16) -#endif - -#ifndef VEC_VUINT16X8 -# define VEC_VUINT16X8 -typedef uint16_t vuint16x8 __attribute__((__vector_size__(16))); -# define VUINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \ - (vuint16x8){ a, b, c, d, e, f, g, h } -# define VUINT16x8_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(u, U, 16, 8) -#endif - -#ifndef VEC_VUINT32X4 -# define VEC_VUINT32X4 -typedef uint32_t vuint32x4 __attribute__((__vector_size__(16))); -# define VUINT32x4_CONSTANT(a, b, c, d) \ - (vuint32x4){ a, b, c, d } -# define VUINT32x4_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(u, U, 32, 4) -#endif - -#ifndef VEC_VUINT64X2 -# define VEC_VUINT64X2 -typedef uint64_t vuint64x2 __attribute__((__vector_size__(16))); -# define VUINT64x2_CONSTANT(a, b) \ - (vuint64x2){ a, b } -# define VUINT64x2_ALIGNMENT 16 -VEC_DEFINE_OPERATIONS(u, U, 64, 2) -#endif - -// ---------------------------------------------------------- - -#undef VEC_DEFINE_OPERATIONS
--- a/include/vec/impl/generic.h Tue Nov 19 15:55:01 2024 -0500 +++ b/include/vec/impl/generic.h Wed Nov 20 04:10:37 2024 -0500 @@ -24,362 +24,91 @@ /* Generic array-based implementation. */ +#ifndef VEC_IMPL_GENERIC_H_ +#define VEC_IMPL_GENERIC_H_ + #include <stdint.h> #include <string.h> -#define VEC_DEFINE_STRUCT(sign, bits, size) \ - typedef struct { \ - sign##int##bits##_t arr[size]; \ - } v##sign##int##bits##x##size; +// ----------------------------------------------------------------- -#define VEC_DEFINE_OPERATIONS(sign, csign, bits, size) \ - VEC_DECL_LOAD_ALIGNED(sign, bits, size) \ +// TODO implement these so we don't waste stack space by doing the +// fallbacks +#define VEC_GENERIC_DEFINE_OPERATIONS_SIGN(sign, csign, bits, size) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load_aligned(const sign##int##bits##_t in[size]) \ { \ v##sign##int##bits##x##size vec; \ - memcpy(vec.arr, in, sizeof(vec.arr)); \ - return vec; \ - } \ - \ - VEC_DECL_LOAD(sign, bits, size) \ - { \ - return v##sign##int##bits##x##size##_load_aligned(in); \ - } \ - \ - VEC_DECL_STORE_ALIGNED(sign, bits, size) \ - { \ - memcpy(out, vec.arr, sizeof(vec.arr)); \ - } \ - \ - VEC_DECL_STORE(sign, bits, size) \ - { \ - return v##sign##int##bits##x##size##_store_aligned(vec, out); \ - } \ - \ - VEC_DECL_ADD(sign, bits, size) \ - { \ - for (int i = 0; i < size; i++) vec1.arr[i] += vec2.arr[i]; \ - return vec1; \ - } \ - \ - VEC_DECL_SUB(sign, bits, size) \ - { \ - for (int i = 0; i < size; i++) vec1.arr[i] -= vec2.arr[i]; \ - return vec1; \ - } \ - \ - VEC_DECL_MUL(sign, bits, size) \ - { \ - for (int i = 0; i < size; i++) vec1.arr[i] *= vec2.arr[i]; \ - return vec1; \ - } \ - \ - VEC_DECL_AND(sign, bits, size) \ - { \ - for (int i = 0; i < size; i++) vec1.arr[i] &= vec2.arr[i]; \ - return vec1; \ - } \ - \ - VEC_DECL_OR(sign, bits, size) \ - { \ - for (int i = 0; i < size; i++) vec1.arr[i] |= vec2.arr[i]; \ - return vec1; \ - } \ - \ - VEC_DECL_XOR(sign, bits, size) \ - { \ - for (int i = 0; i < size; i++) vec1.arr[i] ^= vec2.arr[i]; \ - return vec1; \ - } \ - \ - VEC_GENERIC_SPLAT(sign, csign, bits, size) \ - VEC_GENERIC_SHIFTS(sign, csign, bits, size) \ - VEC_GENERIC_DIVIDE(sign, csign, bits, size) \ - VEC_GENERIC_AVG(sign, bits, size) \ - VEC_GENERIC_COMPARISONS(sign, csign, bits, size) - -#ifndef VEC_VUINT8X16 -# define VEC_VUINT8X16 -VEC_DEFINE_STRUCT(u, 8, 16) -# define VUINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - ((vuint8x16){ .arr = { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p } }) -# define VUINT8x16_ALIGNMENT 1 -VEC_DEFINE_OPERATIONS(u, U, 8, 16) -#endif - -#ifndef VEC_VUINT16X8 -# define VEC_VUINT16X8 -VEC_DEFINE_STRUCT(u, 16, 8) -# define VUINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \ - ((vuint16x8){ .arr = { a, b, c, d, e, f, g, h } }) -# define VUINT16x8_ALIGNMENT 2 -VEC_DEFINE_OPERATIONS(u, U, 16, 8) -#endif - -#ifndef VEC_VUINT32X4 -# define VEC_VUINT32X4 -VEC_DEFINE_STRUCT(u, 32, 4) -# define VUINT32x4_CONSTANT(a, b, c, d) \ - ((vuint32x4){ .arr = { a, b, c, d } }) -# define VUINT32x4_ALIGNMENT 4 -VEC_DEFINE_OPERATIONS(u, U, 32, 4) -#endif - -#ifndef VEC_VUINT64X2 -# define VEC_VUINT64X2 -VEC_DEFINE_STRUCT(u, 64, 2) -# define VUINT64x2_CONSTANT(a, b) \ - ((vuint64x2){ .arr = { a, b } }) -# define VUINT64x2_ALIGNMENT 8 -VEC_DEFINE_OPERATIONS(u, U, 64, 2) -#endif - -#ifndef VEC_VINT16X8 -# define VEC_VINT16X8 -VEC_DEFINE_STRUCT(, 16, 8) -# define VINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \ - ((vint16x8){ .arr = { a, b, c, d, e, f, g, h } }) -# define VINT16x8_ALIGNMENT 2 -VEC_DEFINE_OPERATIONS(, , 16, 8) -#endif - -#ifndef VEC_VINT32X4 -# define VEC_VINT32X4 -VEC_DEFINE_STRUCT(, 32, 4) -# define VINT32x4_CONSTANT(a, b, c, d) \ - ((vint32x4){ .arr = { a, b, c, d } }) -# define VINT32x4_ALIGNMENT 4 -VEC_DEFINE_OPERATIONS(, , 32, 4) -#endif - -#ifndef VEC_VINT64X2 -# define VEC_VINT64X2 -VEC_DEFINE_STRUCT(, 64, 2) -# define VINT64x2_CONSTANT(a, b) \ - ((vint64x2){ .arr = { a, b } }) -# define VINT64x2_ALIGNMENT 8 -VEC_DEFINE_OPERATIONS(, , 64, 2) -#endif - -#undef VEC_DEFINE_STRUCT -#undef VEC_DEFINE_OPERATIONS - -// ----------------------------------------------------------------- -// Okay, now we can implement our "double" structures. -// These use existing structures that are 128 bits in -// size to provide 256-bit or even 512-bit data types. - -#define VEC_DEFINE_STRUCT(sign, bits, size, halfsize) \ - typedef struct { \ - v##sign##int##bits##x##halfsize vecs[2]; \ - } v##sign##int##bits##x##size; - -#define VEC_DEFINE_OP(opcap, op, sign, bits, size, halfsize) \ - VEC_DECL_##opcap(sign, bits, size) \ - { \ - vec1.vecs[0] = v##sign##int##bits##x##halfsize##_##op(vec1.vecs[0], vec2.vecs[0]); \ - vec1.vecs[1] = v##sign##int##bits##x##halfsize##_##op(vec1.vecs[1], vec2.vecs[1]); \ - return vec1; \ - } - -// This could be in way fewer lines, but whatever -#define VEC_DEFINE_OPERATIONS(sign, csign, bits, size, halfsize) \ - VEC_DECL_LOAD_ALIGNED(sign, bits, size) \ - { \ - v##sign##int##bits##x##size vec; \ - vec.vecs[0] = v##sign##int##bits##x##halfsize##_load_aligned(in); \ - vec.vecs[1] = v##sign##int##bits##x##halfsize##_load_aligned(in + halfsize); \ - return vec; \ - } \ - \ - VEC_DECL_LOAD(sign, bits, size) \ - { \ - v##sign##int##bits##x##size vec; \ - vec.vecs[0] = v##sign##int##bits##x##halfsize##_load(in); \ - vec.vecs[1] = v##sign##int##bits##x##halfsize##_load(in + halfsize); \ + memcpy(vec.generic, in, sizeof(sign##int##bits##_t) * size); \ return vec; \ } \ \ - VEC_DECL_SPLAT(sign, bits, size) \ + static void v##sign##int##bits##x##size##_generic_store_aligned(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + memcpy(out, vec.generic, sizeof(sign##int##bits##_t) * size); \ + } \ + \ + static v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_generic = { \ + .load_aligned = v##sign##int##bits##x##size##_generic_load_aligned, \ + .store_aligned = v##sign##int##bits##x##size##_generic_store_aligned, \ + }; + +#define VEC_GENERIC_DEFINE_OPERATIONS(bits, size) \ + VEC_GENERIC_DEFINE_OPERATIONS_SIGN( , , bits, size) \ + VEC_GENERIC_DEFINE_OPERATIONS_SIGN(u, U, bits, size) + +VEC_GENERIC_DEFINE_OPERATIONS(8, 8) +VEC_GENERIC_DEFINE_OPERATIONS(16, 4) +VEC_GENERIC_DEFINE_OPERATIONS(32, 2) +VEC_GENERIC_DEFINE_OPERATIONS(64, 2) + +#undef VEC_GENERIC_DEFINE_OPERATIONS +#undef VEC_GENERIC_DEFINE_OPERATIONS_SIGN + +// ----------------------------------------------------------------- +// now we can just keep doubling the same implementation + +#define VEC_GENERIC_DEFINE_OPERATIONS_SIGN(sign, csign, bits, size, halfsize) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load_aligned(const sign##int##bits##_t in[size]) \ { \ v##sign##int##bits##x##size vec; \ - vec.vecs[0] = v##sign##int##bits##x##halfsize##_splat(x); \ - vec.vecs[1] = v##sign##int##bits##x##halfsize##_splat(x); \ + vec.generic[0] = v##sign##int##bits##x##halfsize##_load_aligned(in); \ + vec.generic[1] = v##sign##int##bits##x##halfsize##_load_aligned(in + halfsize); \ return vec; \ } \ \ - VEC_DECL_STORE_ALIGNED(sign, bits, size) \ + static void v##sign##int##bits##x##size##_generic_store_aligned(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ { \ - v##sign##int##bits##x##halfsize##_store_aligned(vec.vecs[0], out); \ - v##sign##int##bits##x##halfsize##_store_aligned(vec.vecs[1], out + halfsize); \ - } \ - \ - VEC_DECL_STORE(sign, bits, size) \ - { \ - v##sign##int##bits##x##halfsize##_store(vec.vecs[0], out); \ - v##sign##int##bits##x##halfsize##_store(vec.vecs[1], out + halfsize); \ + v##sign##int##bits##x##halfsize##_store_aligned(vec.generic[0], out); \ + v##sign##int##bits##x##halfsize##_store_aligned(vec.generic[1], out + halfsize); \ } \ \ - VEC_DEFINE_OP(ADD, add, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(SUB, sub, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(MUL, mul, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(AND, and, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(OR, or, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(XOR, xor, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(LSHIFT, lshift, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(RSHIFT, rshift, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(LRSHIFT, lrshift, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(DIV, div, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(AVG, avg, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(CMPLT, cmplt, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(CMPGT, cmpgt, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(CMPEQ, cmpeq, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(CMPGE, cmpge, sign, bits, size, halfsize) \ - VEC_DEFINE_OP(CMPLE, cmple, sign, bits, size, halfsize) - -#ifndef VEC_VUINT8X32 -# define VEC_VUINT8X32 -VEC_DEFINE_STRUCT(u, 8, 32, 16) -# define VUINT8x32_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af) \ - ((vuint8x32){ .vecs = { VUINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p), VUINT8x16_CONSTANT(q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af) } }) -# define VUINT8x32_ALIGNMENT VUINT8x16_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 8, 32, 16) -#endif - -#ifndef VEC_VUINT16X16 -# define VEC_VUINT16X16 -VEC_DEFINE_STRUCT(u, 16, 16, 8) -# define VUINT16x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - ((vuint16x16){ .vecs = { VUINT16x8_CONSTANT(a, b, c, d, e, f, g, h), VUINT16x8_CONSTANT(i, j, k, l, m, n, o, p) } }) -# define VUINT16x16_ALIGNMENT VUINT16x8_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 16, 16, 8) -#endif + static v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_generic = { \ + .load_aligned = v##sign##int##bits##x##size##_generic_load_aligned, \ + .store_aligned = v##sign##int##bits##x##size##_generic_store_aligned, \ + }; -#ifndef VEC_VUINT32X8 -# define VEC_VUINT32X8 -VEC_DEFINE_STRUCT(u, 32, 8, 4) -# define VUINT32x8_CONSTANT(a, b, c, d, e, f, g, h) \ - ((vuint32x8){ .vecs = { VUINT32x4_CONSTANT(a, b, c, d), VUINT32x4_CONSTANT(e, f, g, h) } }) -# define VUINT32x8_ALIGNMENT VUINT32x4_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 32, 8, 4) -#endif - -#ifndef VEC_VUINT64X4 -# define VEC_VUINT64X4 -VEC_DEFINE_STRUCT(u, 64, 4, 2) -# define VUINT64x4_CONSTANT(a, b, c, d) \ - ((vuint64x4){ .vecs = { VUINT64x2_CONSTANT(a, b), VUINT64x2_CONSTANT(c, d) } }) -# define VUINT64x4_ALIGNMENT VUINT64x2_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 64, 4, 2) -#endif +#define VEC_GENERIC_DEFINE_OPERATIONS(bits, size, halfsize) \ + VEC_GENERIC_DEFINE_OPERATIONS_SIGN( , , bits, size, halfsize) \ + VEC_GENERIC_DEFINE_OPERATIONS_SIGN(u, U, bits, size, halfsize) -#ifndef VEC_VINT8X32 -# define VEC_VINT8X32 -VEC_DEFINE_STRUCT(, 8, 32, 16) -# define VINT8x32_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af) \ - ((vint8x32){ .vecs = { VINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p), VINT8x16_CONSTANT(q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af) } }) -# define VINT8x32_ALIGNMENT VINT8x16_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 8, 32, 16) -#endif - -#ifndef VEC_VINT16X16 -# define VEC_VINT16X16 -VEC_DEFINE_STRUCT(, 16, 16, 8) -# define VINT16x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - ((vint16x16){ .vecs = { VINT16x8_CONSTANT(a, b, c, d, e, f, g, h), VINT16x8_CONSTANT(i, j, k, l, m, n, o, p) } }) -# define VINT16x16_ALIGNMENT VINT16x8_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 16, 16, 8) -#endif - -#ifndef VEC_VINT32X8 -# define VEC_VINT32X8 -VEC_DEFINE_STRUCT(, 32, 8, 4) -# define VINT32x8_CONSTANT(a, b, c, d, e, f, g, h) \ - ((vuint32x8){ .vecs = { VINT32x4_CONSTANT(a, b, c, d), VINT32x4_CONSTANT(e, f, g, h) } }) -# define VINT32x8_ALIGNMENT VINT32x4_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 32, 8, 4) -#endif +// 128-bit +VEC_GENERIC_DEFINE_OPERATIONS(8, 16, 8) +VEC_GENERIC_DEFINE_OPERATIONS(16, 8, 4) +VEC_GENERIC_DEFINE_OPERATIONS(32, 4, 2) -#ifndef VEC_VINT64X4 -# define VEC_VINT64X4 -VEC_DEFINE_STRUCT(, 64, 4, 2) -# define VINT64x4_CONSTANT(a, b, c, d) \ - ((vint64x4){ .vecs = { VINT64x2_CONSTANT(a, b), VINT64x2_CONSTANT(c, d) } }) -# define VINT64x4_ALIGNMENT VINT64x2_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 64, 4, 2) -#endif - -#ifndef VEC_VUINT8X64 -# define VEC_VUINT8X64 -VEC_DEFINE_STRUCT(u, 8, 64, 32) -# define VUINT8x64_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl) \ - ((vuint8x64){ .vecs = { VUINT8x32_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af), VUINT8x32_CONSTANT(ag, ah, ai, aj, ak, al, am, an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl) } }) -# define VUINT8x64_ALIGNMENT VUINT8x32_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 8, 64, 32) -#endif - -#ifndef VEC_VUINT16X32 -# define VEC_VUINT16X32 -VEC_DEFINE_STRUCT(u, 16, 32, 16) -# define VUINT16x32_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af) \ - ((vuint16x32){ .vecs = { VUINT16x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p), VUINT16x16_CONSTANT(q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af) } }) -# define VUINT16x32_ALIGNMENT VUINT16x16_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 16, 32, 16) -#endif - -#ifndef VEC_VUINT32X16 -# define VEC_VUINT32X16 -VEC_DEFINE_STRUCT(u, 32, 16, 8) -# define VUINT32x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - ((vuint32x16){ .vecs = { VUINT32x8_CONSTANT(a, b, c, d, e, f, g, h), VUINT32x8_CONSTANT(i, j, k, l, m, n, o, p) } }) -# define VUINT32x16_ALIGNMENT VUINT32x8_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 32, 16, 8) -#endif +// 256-bit +VEC_GENERIC_DEFINE_OPERATIONS(8, 32, 16) +VEC_GENERIC_DEFINE_OPERATIONS(16, 16, 8) +VEC_GENERIC_DEFINE_OPERATIONS(32, 8, 4) +VEC_GENERIC_DEFINE_OPERATIONS(64, 4, 2) -#ifndef VEC_VUINT64X8 -# define VEC_VUINT64X8 -VEC_DEFINE_STRUCT(u, 64, 8, 4) -# define VUINT64x8_CONSTANT(a, b, c, d, e, f, g, h) \ - ((vuint64x8){ .vecs = { VUINT64x4_CONSTANT(a, b, c, d), VUINT64x4_CONSTANT(e, f, g, h) } }) -# define VUINT64x8_ALIGNMENT VUINT64x4_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 64, 8, 4) -#endif - -#ifndef VEC_VINT8X64 -# define VEC_VINT8X64 -VEC_DEFINE_STRUCT(, 8, 64, 32) -# define VINT8x64_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl) \ - ((vint8x64){ .vecs = { VINT8x32_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af), VINT8x32_CONSTANT(ag, ah, ai, aj, ak, al, am, an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl) } }) -# define VINT8x64_ALIGNMENT VINT8x32_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 8, 64, 32) -#endif +// 512-bit +VEC_GENERIC_DEFINE_OPERATIONS(8, 64, 32) +VEC_GENERIC_DEFINE_OPERATIONS(16, 32, 16) +VEC_GENERIC_DEFINE_OPERATIONS(32, 16, 8) +VEC_GENERIC_DEFINE_OPERATIONS(64, 8, 4) -#ifndef VEC_VINT16X32 -# define VEC_VINT16X32 -VEC_DEFINE_STRUCT(, 16, 32, 16) -# define VINT16x32_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af) \ - ((vint16x32){ .vecs = { VINT16x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p), VINT16x16_CONSTANT(q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af) } }) -# define VINT16x32_ALIGNMENT VINT16x16_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 16, 32, 16) -#endif +#undef VEC_GENERIC_DEFINE_OPERATIONS +#undef VEC_GENERIC_DEFINE_OPERATIONS_SIGN -#ifndef VEC_VINT32X16 -# define VEC_VINT32X16 -VEC_DEFINE_STRUCT(, 32, 16, 8) -# define VINT32x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - ((vint32x16){ .vecs = { VINT32x8_CONSTANT(a, b, c, d, e, f, g, h), VINT32x8_CONSTANT(i, j, k, l, m, n, o, p) } }) -# define VINT32x16_ALIGNMENT VINT32x8_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 32, 16, 8) -#endif - -#ifndef VEC_VINT64X8 -# define VEC_VINT64X8 -VEC_DEFINE_STRUCT(, 64, 8, 4) -# define VINT64x8_CONSTANT(a, b, c, d, e, f, g, h) \ - ((vint64x8){ .vecs = { VINT64x4_CONSTANT(a, b, c, d), VINT64x4_CONSTANT(e, f, g, h) } }) -# define VINT64x8_ALIGNMENT VINT64x4_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 64, 8, 4) -#endif - -#undef VEC_DEFINE_STRUCT -#undef VEC_DEFINE_OPERATIONS -#undef VEC_DEFINE_OP +#endif /* VEC_IMPL_GENERIC_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/ppc/altivec.h Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,183 @@ +/** + * vec - a tiny SIMD vector library in plain C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +/* Altivec vector support. */ + +#ifndef VEC_IMPL_PPC_ALTIVEC_H_ +#define VEC_IMPL_PPC_ALTIVEC_H_ + +#include <stdint.h> +#include <string.h> + +#include <altivec.h> + +#define VEC_ALTIVEC_ALIGNMENT 16 + +/* GCC 4.2.1 on Mac OS X doesn't have these for some reason */ +#ifdef vec_mul +# define VEC_ALTIVEC_DEFINE_MUL(sign, csign, bits, size) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_mul(vec1.altivec, vec2.altivec) }; \ + } +# define VEC_ALTIVEC_STRUCT_MUL(sign, csign, bits, size) \ + .mul = v##sign##int##bits##x##size##_altivec_mul, +#else +# define VEC_ALTIVEC_DEFINE_MUL(sign, csign, bits, size) +# define VEC_ALTIVEC_STRUCT_MUL(sign, csign, bits, size) +#endif + +#ifdef vec_splats +# define VEC_ALTIVEC_DEFINE_SPLAT(sign, csign, bits, size) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_splat(sign##int##bits##_t x) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_splats(x) }; \ + } +# define VEC_ALTIVEC_STRUCT_SPLAT(sign, csign, bits, size) \ + .splat = v##sign##int##bits##x##size##_altivec_splat, +#else +# define VEC_ALTIVEC_DEFINE_SPLAT(sign, csign, bits, size) +# define VEC_ALTIVEC_STRUCT_SPLAT(sign, csign, bits, size) +#endif + +#define VEC_ALTIVEC_uRSHIFT vec_sr +#define VEC_ALTIVEC_RSHIFT vec_sra + +#define VEC_ALTIVEC_DEFINE_uLRSHIFT(sign, csign, bits, size) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_lrshift(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_sr(vec1.altivec, vec2.altivec) }; \ + } +#define VEC_ALTIVEC_STRUCT_uLRSHIFT(sign, csign, bits, size) \ + .lrshift = v##sign##int##bits##x##size##_altivec_lrshift, + +#define VEC_ALTIVEC_DEFINE_LRSHIFT(sign, csign, bits, size) +#define VEC_ALTIVEC_STRUCT_LRSHIFT(sign, csign, bits, size) + +/* Since altivec conveniently made their API super user friendly, we can just use + * one giant macro to define literally everything */ +#define VEC_DEFINE_OPERATIONS_SIGN(sign, csign, bits, size) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_load_aligned(const sign##int##bits##_t in[size]) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_ld(0, in) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_load(const sign##int##bits##_t in[size]) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_perm(vec_ld(0, in), vec_ld(16, in), vec_lvsl(0, in)) }; \ + } \ + \ + static void v##sign##int##bits##x##size##_altivec_store_aligned(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + vec_st(vec.altivec, 0, out); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_add(vec1.altivec, vec2.altivec) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_sub(vec1.altivec, vec2.altivec) }; \ + } \ + \ + VEC_ALTIVEC_DEFINE_MUL(sign, csign, bits, size) \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_sl(vec1.altivec, vec2.altivec) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = VEC_ALTIVEC_##sign##RSHIFT(vec1.altivec, vec2.altivec) }; \ + } \ + \ + VEC_ALTIVEC_DEFINE_##sign##LRSHIFT(sign, csign, bits, size) \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_avg(vec1.altivec, vec2.altivec) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_and(vec1.altivec, vec2.altivec) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_or(vec1.altivec, vec2.altivec) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .altivec = vec_xor(vec1.altivec, vec2.altivec) }; \ + } \ + \ + VEC_ALTIVEC_DEFINE_SPLAT(sign, csign, bits, size) \ + \ + static v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_altivec = { \ + .load_aligned = v##sign##int##bits##x##size##_altivec_load_aligned, \ + .load = v##sign##int##bits##x##size##_altivec_load, \ + .store_aligned = v##sign##int##bits##x##size##_altivec_store_aligned, \ + .add = v##sign##int##bits##x##size##_altivec_add, \ + .sub = v##sign##int##bits##x##size##_altivec_sub, \ + VEC_ALTIVEC_STRUCT_MUL(sign, csign, bits, size) \ + .lshift = v##sign##int##bits##x##size##_altivec_lshift, \ + .rshift = v##sign##int##bits##x##size##_altivec_rshift, \ + VEC_ALTIVEC_STRUCT_##sign##LRSHIFT(sign, csign, bits, size) \ + .avg = v##sign##int##bits##x##size##_altivec_avg, \ + .and = v##sign##int##bits##x##size##_altivec_and, \ + .or = v##sign##int##bits##x##size##_altivec_or, \ + .xor = v##sign##int##bits##x##size##_altivec_xor, \ + VEC_ALTIVEC_STRUCT_SPLAT(sign, csign, bits, size) \ + }; + +#define VEC_DEFINE_OPERATIONS(bits, size) \ + VEC_DEFINE_OPERATIONS_SIGN( , , bits, size) \ + VEC_DEFINE_OPERATIONS_SIGN(u, U, bits, size) + +VEC_DEFINE_OPERATIONS(8, 16) +VEC_DEFINE_OPERATIONS(16, 8) +VEC_DEFINE_OPERATIONS(32, 4) +#ifdef VEC_COMPILER_HAS_ALTIVEC_VSX +VEC_DEFINE_OPERATIONS(64, 2) +#endif + +#undef VEC_DEFINE_OPERATIONS +#undef VEC_DEFINE_OPERATIONS_SIGN +#undef VEC_ALTIVEC_DEFINE_MUL +#undef VEC_ALTIVEC_STRUCT_MUL +#undef VEC_ALTIVEC_DEFINE_LRSHIFT +#undef VEC_ALTIVEC_STRUCT_LRSHIFT +#undef VEC_ALTIVEC_DEFINE_uLRSHIFT +#undef VEC_ALTIVEC_STRUCT_uLRSHIFT +#undef VEC_ALTIVEC_DEFINE_SPLAT +#undef VEC_ALTIVEC_STRUCT_SPLAT +#undef VEC_ALTIVEC_uRSHIFT +#undef VEC_ALTIVEC_RSHIFT + +#endif /* VEC_IMPL_PPC_ALTIVEC_H_ */
--- a/include/vec/impl/sse2.h Tue Nov 19 15:55:01 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,225 +0,0 @@ -/** - * vec - a tiny SIMD vector library in plain C99 - * - * Copyright (c) 2024 Paper - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -**/ - -#include <emmintrin.h> - -#define VEC_SSE2_ALIGNMENT 16 - -#define VEC_SSE2_MUL_8x16(sign) \ - VEC_DECL_MUL(sign, 8, 16) \ - { \ - /* unpack and multiply */ \ - __m128i dst_even = _mm_mullo_epi16(vec1, vec2); \ - __m128i dst_odd = _mm_mullo_epi16(_mm_srli_epi16(vec1, 8), _mm_srli_epi16(vec2, 8)); \ - \ - /* repack */ \ - return _mm_or_si128( \ - _mm_slli_epi16(dst_odd, 8), \ - _mm_srli_epi16(_mm_slli_epi16(dst_even, 8), 8) \ - ); \ - } - -#define VEC_SSE2_MUL_16x8(sign) \ - VEC_DECL_MUL(sign, 16, 8) \ - { \ - /* we have a real instruction for this */ \ - return _mm_mullo_epi16(vec1, vec2); \ - } - -#define VEC_SSE2_MUL_32x4(sign) \ - VEC_DECL_MUL(sign, 32, 4) \ - { \ - /* this was stolen from... somewhere :) */ \ - __m128i a13 = _mm_shuffle_epi32(vec1, 0xF5); /* (-,a3,-,a1) */ \ - __m128i b13 = _mm_shuffle_epi32(vec2, 0xF5); /* (-,b3,-,b1) */ \ - __m128i prod02 = _mm_mul_epu32(vec1, vec2); /* (-,a2*b2,-,a0*b0) */ \ - __m128i prod13 = _mm_mul_epu32(a13, b13); /* (-,a3*b3,-,a1*b1) */ \ - __m128i prod01 = _mm_unpacklo_epi32(prod02,prod13); /* (-,-,a1*b1,a0*b0) */ \ - __m128i prod23 = _mm_unpackhi_epi32(prod02,prod13); /* (-,-,a3*b3,a2*b2) */ \ - return _mm_unpacklo_epi64(prod01, prod23); /* (ab3,ab2,ab1,ab0) */ \ - } - -#define VEC_SSE2_MUL_64x2(sign) \ - VEC_DECL_MUL(sign, 64, 2) \ - { \ - __m128i ac = _mm_mul_epu32(vec1, vec2); /* ac = (vec1 & UINT32_MAX) * (vec2 & UINT32_MAX); */ \ - __m128i b = _mm_srli_epi64(vec1, 32); /* b = vec1 >> 32; */ \ - __m128i bc = _mm_mul_epu32(b, vec2); /* bc = b * (vec2 & UINT32_MAX); */ \ - __m128i d = _mm_srli_epi64(vec2, 32); /* d = vec2 >> 32; */ \ - __m128i ad = _mm_mul_epu32(vec1, d); /* ad = (vec1 & UINT32_MAX) * d; */ \ - __m128i hi = _mm_add_epi64(bc, ad); /* hi = bc + ad; */ \ - hi = _mm_slli_epi64(hi, 32); /* hi <<= 32; */ \ - return _mm_add_epi64(hi, ac); /* return ac + hi; */ \ - } - -#define VEC_DEFINE_OPERATIONS(sign, csign, bits, size) \ - VEC_DECL_LOAD_ALIGNED(sign, bits, size) \ - { \ - return _mm_load_si128((const __m128i *)in); \ - } \ - \ - VEC_DECL_LOAD(sign, bits, size) \ - { \ - return _mm_loadu_si128((const __m128i *)in); \ - } \ - \ - VEC_DECL_STORE_ALIGNED(sign, bits, size) \ - { \ - _mm_store_si128((__m128i *)out, vec); \ - } \ - \ - VEC_DECL_STORE(sign, bits, size) \ - { \ - _mm_storeu_si128((__m128i *)out, vec); \ - } \ - \ - VEC_DECL_ADD(sign, bits, size) \ - { \ - return _mm_add_epi##bits(vec1, vec2); \ - } \ - \ - VEC_DECL_SUB(sign, bits, size) \ - { \ - return _mm_sub_epi##bits(vec1, vec2); \ - } \ - \ - VEC_DECL_AND(sign, bits, size) \ - { \ - return _mm_and_si128(vec1, vec2); \ - } \ - \ - VEC_DECL_OR(sign, bits, size) \ - { \ - return _mm_or_si128(vec1, vec2); \ - } \ - \ - VEC_DECL_XOR(sign, bits, size) \ - { \ - return _mm_xor_si128(vec1, vec2); \ - } \ - \ - VEC_SSE2_MUL_##bits##x##size(sign) \ - \ - VEC_GENERIC_SPLAT(sign, csign, bits, size) \ - VEC_GENERIC_DIVIDE(sign, csign, bits, size) \ - VEC_GENERIC_SHIFTS(sign, csign, bits, size) \ - VEC_GENERIC_AVG(sign, bits, size) - -#define VEC_DEFINE_COMPARISONS_SIGNED(bits, size) \ - VEC_DECL_CMPEQ(, bits, size) \ - { \ - return _mm_cmpeq_epi##bits(vec1, vec2); \ - } \ - VEC_DECL_CMPLT(, bits, size) \ - { \ - return _mm_cmplt_epi##bits(vec1, vec2); \ - } \ - VEC_DECL_CMPGT(, bits, size) \ - { \ - return _mm_cmpgt_epi##bits(vec1, vec2); \ - } \ - VEC_GENERIC_THAN_OR_EQUAL(, bits, size) - -#ifndef VEC_VUINT16X8 -# define VEC_VUINT16X8 -typedef __m128i vuint16x8; -# define VUINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \ - (_mm_setr_epi16(h, g, f, e, d, c, b, a)) -# define VUINT16x8_ALIGNMENT VEC_SSE2_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 16, 8) -VEC_GENERIC_COMPARISONS(u, U, 16, 8) -#endif - -#ifndef VEC_VUINT32X4 -# define VEC_VUINT32X4 -typedef __m128i vuint32x4; -# define VUINT32x4_CONSTANT(a, b, c, d) \ - (_mm_setr_epi32(d, c, b, a)) -# define VUINT32x4_ALIGNMENT VEC_SSE2_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 32, 4) -VEC_GENERIC_COMPARISONS(u, U, 32, 4) -#endif - -#ifndef VEC_VUINT64X2 -# define VEC_VUINT64X2 -typedef __m128i vuint64x2; -VEC_FUNC_KEYWORDS vuint64x2 VUINT64x2_CONSTANT(uint64_t a, uint64_t b) -{ - return _mm_setr_epi32(b, b >> 32, a, a >> 32); -} -# define VUINT64x2_ALIGNMENT VEC_SSE2_ALIGNMENT -VEC_DEFINE_OPERATIONS(u, U, 64, 2) -VEC_GENERIC_COMPARISONS(u, U, 64, 2) -#endif - -#ifndef VEC_VINT8X16 -# define VEC_VINT8X16 -typedef __m128i vint8x16; -# define VINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ - (_mm_setr_epi8(p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a)) -# define VINT8x16_ALIGNMENT VEC_SSE2_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 8, 16) -VEC_DEFINE_COMPARISONS_SIGNED(8, 16) -#endif - -#ifndef VEC_VINT16X8 -# define VEC_VINT16X8 -typedef __m128i vint16x8; -# define VINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \ - (_mm_setr_epi16(h, g, f, e, d, c, b, a)) -# define VINT16x8_ALIGNMENT VEC_SSE2_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 16, 8) -VEC_DEFINE_COMPARISONS_SIGNED(16, 8) -#endif - -#ifndef VEC_VINT32X4 -# define VEC_VINT32X4 -typedef __m128i vint32x4; -# define VINT32x4_CONSTANT(a, b, c, d) \ - (_mm_setr_epi32(d, c, b, a)) -# define VINT32x4_ALIGNMENT VEC_SSE2_ALIGNMENT -VEC_DEFINE_OPERATIONS(, , 32, 4) -VEC_DEFINE_COMPARISONS_SIGNED(32, 4) -#endif - -#ifndef VEC_VINT64X2 -# define VEC_VINT64X2 -typedef __m128i vint64x2; -# define VINT64x2_ALIGNMENT VEC_SSE2_ALIGNMENT -VEC_FUNC_KEYWORDS vint64x2 VINT64x2_CONSTANT(int64_t a, int64_t b) -{ - return _mm_setr_epi32(b, vec_rshift(b, 32), a, vec_rshift(a, 32)); -} -VEC_DEFINE_OPERATIONS(, , 64, 2) -VEC_GENERIC_COMPARISONS(, , 64, 2) -#endif - -#undef VEC_DEFINE_OPERATIONS -#undef VEC_DEFINE_COMPARISONS_SIGNED - -/* multiply */ -#undef VEC_SSE2_MUL_8x16 -#undef VEC_SSE2_MUL_16x8 -#undef VEC_SSE2_MUL_32x4 -#undef VEC_SSE2_MUL_64x2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/x86/avx2.h Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,257 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_X86_AVX2_H_ +#define VEC_IMPL_X86_AVX2_H_ + +#define VEC_AVX2_OPERATION_8x32_16x16(op, sign) \ + do { \ + /* unpack and multiply */ \ + __m256i dst_even = _mm256_##op##_epi16(vec1.avx2, vec2.avx2); \ + __m256i dst_odd = _mm256_##op##_epi16(_mm256_srli_epi16(vec1.avx2, 8), _mm256_srli_epi16(vec2.avx2, 8)); \ + \ + /* repack */ \ + return (v##sign##int8x32){ .avx2 = _mm256_or_si256( \ + _mm256_slli_epi16(dst_odd, 8), \ + _mm256_srli_epi16(_mm256_slli_epi16(dst_even, 8), 8) \ + )}; \ + } while (0) + +#define VEC_AVX2_OPERATION_8x32_32x8(op, sign) \ + do { \ + /* unpack */ \ + __m256i dst_1 = _mm256_##op##_epi32(vec1.avx2, vec2.avx2); \ + __m256i dst_2 = _mm256_##op##_epi32(_mm256_srli_epi32(vec1.avx2, 8), _mm256_srli_epi32(vec2.avx2, 8)); \ + __m256i dst_3 = _mm256_##op##_epi32(_mm256_srli_epi32(vec1.avx2, 16), _mm256_srli_epi32(vec2.avx2, 16)); \ + __m256i dst_4 = _mm256_##op##_epi32(_mm256_srli_epi32(vec1.avx2, 24), _mm256_srli_epi32(vec2.avx2, 24)); \ + \ + /* repack */ \ + return (v##sign##int8x32){ .avx2 = _mm256_or_si256( \ + _mm256_or_si256( \ + _mm256_slli_epi32(dst_4, 8), \ + _mm256_srli_epi32(_mm256_slli_epi32(dst_3, 8), 8) \ + ), \ + _mm256_or_si256( \ + _mm256_slli_epi32(_mm256_slli_epi32(dst_2, 8), 16), \ + _mm256_srli_epi32(_mm256_slli_epi32(dst_1, 8), 24) \ + ) \ + )}; \ + } while (0) + +#define VEC_AVX2_OPERATION_16x16(op, sign) \ + do { \ + /* unpack and multiply */ \ + __m256i dst_even = _mm256_##op##_epi32(vec1.avx2, vec2.avx2); \ + __m256i dst_odd = _mm256_##op##_epi32(_mm256_srli_epi32(vec1.avx2, 16), _mm256_srli_epi32(vec2.avx2, 16)); \ + \ + /* repack */ \ + return (v##sign##int16x16){ .avx2 = _mm256_or_si256( \ + _mm256_slli_epi32(dst_odd, 16), \ + _mm256_srli_epi32(_mm256_slli_epi16(dst_even, 16), 16) \ + )}; \ + } while (0) + +// shifting + +#define VEC_AVX2_LSHIFT_8x32(sign) \ + VEC_AVX2_OPERATION_8x32_32x8(sllv, sign) + +#define VEC_AVX2_LSHIFT_16x16(sign) \ + VEC_AVX2_OPERATION_16x16(sllv, sign) + +#define VEC_AVX2_LSHIFT_32x8(sign) \ + do { \ + return (v##sign##int32x8){ .avx2 = _mm256_sllv_epi32(vec1.avx2, vec2.avx2) }; \ + } while (0) + +#define VEC_AVX2_LSHIFT_64x4(sign) \ + do { \ + return (v##sign##int64x4){ .avx2 = _mm256_sllv_epi64(vec1.avx2, vec2.avx2) }; \ + } while (0) + +#define VEC_AVX2_RSHIFT_8x32(sign, aORl) \ + VEC_AVX2_OPERATION_8x32_32x8(sr##aORl##v, sign) + +#define VEC_AVX2_RSHIFT_16x16(sign, aORl) \ + VEC_AVX2_OPERATION_16x16(sr##aORl##v, sign) + +#define VEC_AVX2_RSHIFT_32x8(sign, aORl) \ + do { \ + return (v##sign##int32x8){ .avx2 = _mm256_sr##aORl##v_epi32(vec1.avx2, vec2.avx2) }; \ + } while (0) + +#define VEC_AVX2_aRSHIFT_64x4(sign) \ + do { \ + return v##sign##int64x4_fallback_rshift(vec1, vec2); \ + } while (0) + +#define VEC_AVX2_lRSHIFT_64x4(sign) \ + do { \ + return (v##sign##int64x4){ .avx2 = _mm256_srlv_epi64(vec1.avx2, vec2.avx2) }; \ + } while (0) + +#define VEC_AVX2_RSHIFT_64x4(sign, aORl) \ + VEC_AVX2_##aORl##RSHIFT_64x4(sign) + +// multiplication + +#define VEC_AVX2_MUL_8x32(sign) \ + VEC_AVX2_OPERATION_8x32_16x16(mullo, sign) + +#define VEC_AVX2_MUL_16x16(sign) \ + do { \ + return (v##sign##int16x16){ .avx2 = _mm256_mullo_epi16(vec1.avx2, vec2.avx2) }; \ + } while (0) + +#define VEC_AVX2_MUL_32x8(sign) \ + do { \ + return (v##sign##int32x8) { .avx2 = _mm256_mullo_epi32(vec1.avx2, vec2.avx2) }; \ + } while (0) + +#define VEC_AVX2_MUL_64x4(sign) \ + do { \ + __m256i ac = _mm256_mul_epu32(vec1.avx2, vec2.avx2); \ + __m256i b = _mm256_srli_epi64(vec1.avx2, 32); \ + __m256i bc = _mm256_mul_epu32(b, vec2.avx2); \ + __m256i d = _mm256_srli_epi64(vec2.avx2, 32); \ + __m256i ad = _mm256_mul_epu32(vec1.avx2, d); \ + __m256i hi = _mm256_add_epi64(bc, ad); \ + hi = _mm256_slli_epi64(hi, 32); \ + return (v##sign##int64x4) { .avx2 = _mm256_add_epi64(hi, ac) }; \ + } while (0) + +// operations + +#define VEC_AVX2_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_load_aligned(const sign##int##bits##_t in[size]) \ + { \ + return (v##sign##int##bits##x##size) { .avx2 = _mm256_load_si256((const __m256i *)in) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_load(const sign##int##bits##_t in[size]) \ + { \ + return (v##sign##int##bits##x##size) { .avx2 = _mm256_loadu_si256((const __m256i *)in) }; \ + } \ + \ + static void v##sign##int##bits##x##size##_avx2_store_aligned(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + _mm256_store_si256((__m256i *)out, vec.avx2); \ + } \ + \ + static void v##sign##int##bits##x##size##_avx2_store(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + _mm256_storeu_si256((__m256i *)out, vec.avx2); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .avx2 = _mm256_add_epi##bits(vec1.avx2, vec2.avx2) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .avx2 = _mm256_sub_epi##bits(vec1.avx2, vec2.avx2) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_AVX2_MUL_##bits##x##size(sign); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .avx2 = _mm256_and_si256(vec1.avx2, vec2.avx2) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .avx2 = _mm256_or_si256(vec1.avx2, vec2.avx2) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .avx2 = _mm256_xor_si256(vec1.avx2, vec2.avx2) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_AVX2_LSHIFT_##bits##x##size(sign); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_AVX2_RSHIFT_##bits##x##size(sign, a); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_AVX2_RSHIFT_##bits##x##size(sign, l); \ + } \ + \ + static v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_avx2 = { \ + .load_aligned = v##sign##int##bits##x##size##_avx2_load_aligned, \ + .load = v##sign##int##bits##x##size##_avx2_load, \ + .store_aligned = v##sign##int##bits##x##size##_avx2_store_aligned, \ + .store = v##sign##int##bits##x##size##_avx2_store, \ + .add = v##sign##int##bits##x##size##_avx2_add, \ + .sub = v##sign##int##bits##x##size##_avx2_sub, \ + .mul = v##sign##int##bits##x##size##_avx2_mul, \ + .and = v##sign##int##bits##x##size##_avx2_and, \ + .or = v##sign##int##bits##x##size##_avx2_or, \ + .xor = v##sign##int##bits##x##size##_avx2_xor, \ + .lshift = v##sign##int##bits##x##size##_avx2_lshift, \ + .rshift = v##sign##int##bits##x##size##_avx2_rshift, \ + .lrshift = v##sign##int##bits##x##size##_avx2_lrshift, \ + }; + +#define VEC_AVX2_DEFINE_OPERATIONS(bits, size) \ + VEC_AVX2_DEFINE_OPERATIONS_SIGN( , bits, size) \ + VEC_AVX2_DEFINE_OPERATIONS_SIGN(u, bits, size) + +VEC_AVX2_DEFINE_OPERATIONS(8, 32) +VEC_AVX2_DEFINE_OPERATIONS(16, 16) +VEC_AVX2_DEFINE_OPERATIONS(32, 8) +VEC_AVX2_DEFINE_OPERATIONS(64, 4) + +#undef VEC_AVX2_DEFINE_OPERATIONS +#undef VEC_AVX2_DEFINE_OPERATIONS_SIGN +#undef VEC_AVX2_MUL_8x32 +#undef VEC_AVX2_MUL_16x16 +#undef VEC_AVX2_MUL_32x8 +#undef VEC_AVX2_MUL_64x4 +#undef VEC_AVX2_OPERATION_8x32_16x16 +#undef VEC_AVX2_OPERATION_8x32_32x8 +#undef VEC_AVX2_OPERATION_16x16 +#undef VEC_AVX2_LSHIFT_8x32 +#undef VEC_AVX2_LSHIFT_16x16 +#undef VEC_AVX2_LSHIFT_32x8 +#undef VEC_AVX2_LSHIFT_64x4 +#undef VEC_AVX2_RSHIFT_8x32 +#undef VEC_AVX2_RSHIFT_16x16 +#undef VEC_AVX2_RSHIFT_32x8 +#undef VEC_AVX2_aRSHIFT_64x4 +#undef VEC_AVX2_lRSHIFT_64x4 +#undef VEC_AVX2_RSHIFT_64x4 + +#endif /* VEC_IMPL_X86_AVX2_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/x86/avx512f.h Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,254 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_X86_AVX512F_H_ +#define VEC_IMPL_X86_AVX512F_H_ + +#define VEC_AVX512F_OPERATION_8x64(op, sign) \ + do { \ + /* unpack and add */ \ + __m512i dst_1 = _mm512_##op##_epi32(vec1.avx512f, vec2.avx512f); \ + __m512i dst_2 = _mm512_##op##_epi32(_mm512_srli_epi32(vec1.avx512f, 8), _mm512_srli_epi32(vec2.avx512f, 8)); \ + __m512i dst_3 = _mm512_##op##_epi32(_mm512_srli_epi32(vec1.avx512f, 16), _mm512_srli_epi32(vec2.avx512f, 16)); \ + __m512i dst_4 = _mm512_##op##_epi32(_mm512_srli_epi32(vec1.avx512f, 24), _mm512_srli_epi32(vec2.avx512f, 24)); \ + \ + /* repack */ \ + return (v##sign##int8x64){ .avx512f = _mm512_or_si512( \ + _mm512_or_si512( \ + _mm512_slli_epi32(dst_4, 8), \ + _mm512_srli_epi32(_mm512_slli_epi32(dst_3, 8), 8) \ + ), \ + _mm512_or_si512( \ + _mm512_slli_epi32(_mm512_slli_epi32(dst_2, 8), 16), \ + _mm512_srli_epi32(_mm512_slli_epi32(dst_1, 8), 24) \ + ) \ + )}; \ + } while (0) + +#define VEC_AVX512F_OPERATION_16x32(op, sign) \ + do { \ + /* unpack and add */ \ + __m512i dst_even = _mm512_##op##_epi32(vec1.avx512f, vec2.avx512f); \ + __m512i dst_odd = _mm512_##op##_epi32(_mm512_srli_epi32(vec1.avx512f, 16), _mm512_srli_epi32(vec2.avx512f, 16)); \ + \ + /* repack */ \ + return (v##sign##int16x32){ .avx512f = _mm512_or_si512( \ + _mm512_slli_epi32(dst_odd, 16), \ + _mm512_srli_epi32(_mm512_slli_epi32(dst_even, 16), 16) \ + )}; \ + } while (0) + +#define VEC_AVX512F_ADD_8x64(sign) \ + VEC_AVX512F_OPERATION_8x64(add, sign) + +#define VEC_AVX512F_ADD_16x32(sign) \ + VEC_AVX512F_OPERATION_16x32(add, sign) + +#define VEC_AVX512F_ADD_32x16(sign) \ + do { \ + return (v##sign##int32x16) { .avx512f = _mm512_add_epi32(vec1.avx512f, vec2.avx512f) }; \ + } while (0) + +#define VEC_AVX512F_ADD_64x8(sign) \ + do { \ + return (v##sign##int64x8) { .avx512f = _mm512_add_epi64(vec1.avx512f, vec2.avx512f) }; \ + } while (0) + +#define VEC_AVX512F_SUB_8x64(sign) \ + VEC_AVX512F_OPERATION_8x64(sub, sign) + +#define VEC_AVX512F_SUB_16x32(sign) \ + VEC_AVX512F_OPERATION_16x32(sub, sign) + +#define VEC_AVX512F_SUB_32x16(sign) \ + do { \ + return (v##sign##int32x16) { .avx512f = _mm512_sub_epi32(vec1.avx512f, vec2.avx512f) }; \ + } while (0) + +#define VEC_AVX512F_SUB_64x8(sign) \ + do { \ + return (v##sign##int64x8) { .avx512f = _mm512_sub_epi64(vec1.avx512f, vec2.avx512f) }; \ + } while (0) + +#define VEC_AVX512F_MUL_8x64(sign) \ + VEC_AVX512F_OPERATION_8x64(mullo, sign) + +#define VEC_AVX512F_MUL_16x32(sign) \ + VEC_AVX512F_OPERATION_16x32(mullo, sign) + +#define VEC_AVX512F_MUL_32x16(sign) \ + do { \ + return (v##sign##int32x16) { .avx512f = _mm512_mullo_epi32(vec1.avx512f, vec2.avx512f) }; \ + } while (0) + +#define VEC_AVX512F_MUL_64x8(sign) \ + do { \ + __m512i ac = _mm512_mul_epu32(vec1.avx512f, vec2.avx512f); \ + __m512i b = _mm512_srli_epi64(vec1.avx512f, 32); \ + __m512i bc = _mm512_mul_epu32(b, vec2.avx512f); \ + __m512i d = _mm512_srli_epi64(vec2.avx512f, 32); \ + __m512i ad = _mm512_mul_epu32(vec1.avx512f, d); \ + __m512i hi = _mm512_add_epi64(bc, ad); \ + hi = _mm512_slli_epi64(hi, 32); \ + return (v##sign##int64x8) { .avx512f = _mm512_add_epi64(hi, ac) }; \ + } while (0) + +#define VEC_AVX512F_LSHIFT_8x64(sign) \ + VEC_AVX512F_OPERATION_8x64(sllv, sign) + +#define VEC_AVX512F_LSHIFT_16x32(sign) \ + VEC_AVX512F_OPERATION_16x32(sllv, sign) + +#define VEC_AVX512F_LSHIFT_32x16(sign) \ + do { \ + return (v##sign##int32x16){ .avx512f = _mm512_sllv_epi32(vec1.avx512f, vec2.avx512f) }; \ + } while (0) + +#define VEC_AVX512F_LSHIFT_64x8(sign) \ + do { \ + return (v##sign##int64x8){ .avx512f = _mm512_sllv_epi64(vec1.avx512f, vec2.avx512f) }; \ + } while (0) + +#define VEC_AVX512F_RSHIFT_8x64(sign, aORl) \ + VEC_AVX512F_OPERATION_8x64(sr##aORl##v, sign) + +#define VEC_AVX512F_RSHIFT_16x32(sign, aORl) \ + VEC_AVX512F_OPERATION_16x32(sr##aORl##v, sign) + +#define VEC_AVX512F_RSHIFT_32x16(sign, aORl) \ + do { \ + return (v##sign##int32x16){ .avx512f = _mm512_sr##aORl##v_epi32(vec1.avx512f, vec2.avx512f) }; \ + } while (0) + +#define VEC_AVX512F_RSHIFT_64x8(sign, aORl) \ + do { \ + return (v##sign##int64x8){ .avx512f = _mm512_sr##aORl##v_epi64(vec1.avx512f, vec2.avx512f) }; \ + } while (0) + +#define VEC_AVX512F_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_load_aligned(const sign##int##bits##_t in[size]) \ + { \ + return (v##sign##int##bits##x##size) { .avx512f = _mm512_load_si512((const __m512i *)in) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_load(const sign##int##bits##_t in[size]) \ + { \ + return (v##sign##int##bits##x##size) { .avx512f = _mm512_loadu_si512((const __m512i *)in) }; \ + } \ + \ + static void v##sign##int##bits##x##size##_avx512f_store_aligned(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + _mm512_store_si512((__m512i *)out, vec.avx512f); \ + } \ + \ + static void v##sign##int##bits##x##size##_avx512f_store(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + _mm512_storeu_si512((__m512i *)out, vec.avx512f); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_AVX512F_ADD_##bits##x##size(sign); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_AVX512F_SUB_##bits##x##size(sign); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_AVX512F_MUL_##bits##x##size(sign); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .avx512f = _mm512_and_si512(vec1.avx512f, vec2.avx512f) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .avx512f = _mm512_or_si512(vec1.avx512f, vec2.avx512f) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .avx512f = _mm512_xor_si512(vec1.avx512f, vec2.avx512f) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_AVX512F_LSHIFT_##bits##x##size(sign); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_AVX512F_RSHIFT_##bits##x##size(sign, a); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_AVX512F_RSHIFT_##bits##x##size(sign, l); \ + } \ + \ + static v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_avx512f = { \ + .load_aligned = v##sign##int##bits##x##size##_avx512f_load_aligned, \ + .load = v##sign##int##bits##x##size##_avx512f_load, \ + .store_aligned = v##sign##int##bits##x##size##_avx512f_store_aligned, \ + .store = v##sign##int##bits##x##size##_avx512f_store, \ + .add = v##sign##int##bits##x##size##_avx512f_add, \ + .sub = v##sign##int##bits##x##size##_avx512f_sub, \ + .mul = v##sign##int##bits##x##size##_avx512f_mul, \ + .and = v##sign##int##bits##x##size##_avx512f_and, \ + .or = v##sign##int##bits##x##size##_avx512f_or, \ + .xor = v##sign##int##bits##x##size##_avx512f_xor, \ + }; + +#define VEC_AVX512F_DEFINE_OPERATIONS(bits, size) \ + VEC_AVX512F_DEFINE_OPERATIONS_SIGN( , bits, size) \ + VEC_AVX512F_DEFINE_OPERATIONS_SIGN(u, bits, size) + +VEC_AVX512F_DEFINE_OPERATIONS(8, 64) +VEC_AVX512F_DEFINE_OPERATIONS(16, 32) +VEC_AVX512F_DEFINE_OPERATIONS(32, 16) +VEC_AVX512F_DEFINE_OPERATIONS(64, 8) + +#undef VEC_AVX512F_DEFINE_OPERATIONS +#undef VEC_AVX512F_DEFINE_OPERATIONS_SIGN +#undef VEC_AVX512F_MUL_8x64 +#undef VEC_AVX512F_MUL_16x32 +#undef VEC_AVX512F_MUL_32x16 +#undef VEC_AVX512F_MUL_64x8 + +#undef VEC_AVX512F_LSHIFT_8x64 +#undef VEC_AVX512F_LSHIFT_16x32 +#undef VEC_AVX512F_LSHIFT_32x16 +#undef VEC_AVX512F_LSHIFT_64x8 + +#undef VEC_AVX512F_RSHIFT_8x64 +#undef VEC_AVX512F_RSHIFT_16x32 +#undef VEC_AVX512F_RSHIFT_32x16 +#undef VEC_AVX512F_RSHIFT_64x8 + +#endif /* VEC_IMPL_X86_AVX512F_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/x86/mmx.h Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,185 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_X86_MMX_H_ +#define VEC_IMPL_X86_MMX_H_ + +#define VEC_MMX_OPERATION_8x8(op, sign) \ + do { \ + /* unpack and multiply */ \ + __m64 dst_even = _mm_##op##_pi16(vec1.mmx, vec2.mmx); \ + __m64 dst_odd = _mm_##op##_pi16(_mm_srli_pi16(vec1.mmx, 8), _mm_srli_pi16(vec2.mmx, 8)); \ + \ + /* repack */ \ + return (v##sign##int8x8){ .mmx = _mm_or_si64( \ + _mm_slli_pi16(dst_odd, 8), \ + _mm_srli_pi16(_mm_slli_pi16(dst_even, 8), 8) \ + )}; \ + } while (0) + +// shifting +#define VEC_MMX_LSHIFT_8x8(sign) \ + VEC_MMX_OPERATION_8x8(sll, sign) + +#define VEC_MMX_LSHIFT_16x4(sign) \ + do { \ + return (v##sign##int16x4){ .mmx = _mm_sll_pi16(vec1.mmx, vec2.mmx) }; \ + } while (0) + +#define VEC_MMX_LSHIFT_32x2(sign) \ + do { \ + return (v##sign##int32x2){ .mmx = _mm_sll_pi32(vec1.mmx, vec2.mmx) }; \ + } while (0) + +#define VEC_MMX_RSHIFT_8x8(sign, aORl) \ + VEC_MMX_OPERATION_8x8(sr##aORl, sign) + +#define VEC_MMX_RSHIFT_16x4(sign, aORl) \ + do { \ + return (v##sign##int16x4){ .mmx = _mm_sr##aORl##_pi16(vec1.mmx, vec2.mmx) }; \ + } while (0) + +#define VEC_MMX_RSHIFT_32x2(sign, aORl) \ + do { \ + return (v##sign##int32x2){ .mmx = _mm_sr##aORl##_pi32(vec1.mmx, vec2.mmx) }; \ + } while (0) + +// shared between MMX variations +#define VEC_MMX_MUL_8x8(sign) \ + VEC_MMX_OPERATION_8x8(mullo, sign) + +#define VEC_MMX_MUL_16x4(sign) \ + do { \ + /* we have a real instruction for this */ \ + return (v##sign##int16x4){ .mmx = _mm_mullo_pi16(vec1.mmx, vec2.mmx) }; \ + } while (0) + +#define VEC_MMX_MUL_32x2(sign) \ + do { \ + __m64 ac = _mm_mullo_pi16(vec1.mmx, vec2.mmx); \ + __m64 b = _mm_srli_pi32(vec1.mmx, 16); \ + __m64 bc = _mm_mullo_pi16(b, vec2.mmx); \ + __m64 d = _mm_srli_pi32(vec2.mmx, 16); \ + __m64 ad = _mm_mullo_pi16(vec1.mmx, d); \ + __m64 hi = _mm_add_pi32(bc, ad); \ + hi = _mm_slli_pi32(hi, 16); \ + return (v##sign##int32x2) { .mmx = _mm_add_pi32(hi, ac) }; /* return ac + hi; */ \ + } while (0) + +#define VEC_MMX_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_load_aligned(const sign##int##bits##_t in[size]) \ + { \ + v##sign##int##bits##x##size vec; \ + memcpy(&vec.mmx, in, sizeof(vec.mmx)); \ + return vec; \ + } \ + \ + static void v##sign##int##bits##x##size##_mmx_store_aligned(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + memcpy(out, &vec.mmx, sizeof(vec.mmx)); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .mmx = _mm_add_pi##bits(vec1.mmx, vec2.mmx) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .mmx = _mm_sub_pi##bits(vec1.mmx, vec2.mmx) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_MMX_MUL_##bits##x##size(sign); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .mmx = _mm_and_si64(vec1.mmx, vec2.mmx) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .mmx = _mm_or_si64(vec1.mmx, vec2.mmx) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .mmx = _mm_xor_si64(vec1.mmx, vec2.mmx) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_MMX_LSHIFT_##bits##x##size(sign); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_MMX_RSHIFT_##bits##x##size(sign, a); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_MMX_RSHIFT_##bits##x##size(sign, l); \ + } \ + \ + static v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_mmx = { \ + .load_aligned = v##sign##int##bits##x##size##_mmx_load_aligned, \ + .load = v##sign##int##bits##x##size##_mmx_load_aligned, \ + .store_aligned = v##sign##int##bits##x##size##_mmx_store_aligned, \ + .store = v##sign##int##bits##x##size##_mmx_store_aligned, \ + .add = v##sign##int##bits##x##size##_mmx_add, \ + .sub = v##sign##int##bits##x##size##_mmx_sub, \ + .mul = v##sign##int##bits##x##size##_mmx_mul, \ + .and = v##sign##int##bits##x##size##_mmx_and, \ + .or = v##sign##int##bits##x##size##_mmx_or, \ + .xor = v##sign##int##bits##x##size##_mmx_xor, \ + .lshift = v##sign##int##bits##x##size##_mmx_lshift, \ + .rshift = v##sign##int##bits##x##size##_mmx_rshift, \ + .lrshift = v##sign##int##bits##x##size##_mmx_lrshift, \ + }; + +#define VEC_MMX_DEFINE_OPERATIONS(bits, size) \ + VEC_MMX_DEFINE_OPERATIONS_SIGN( , bits, size) \ + VEC_MMX_DEFINE_OPERATIONS_SIGN(u, bits, size) + +VEC_MMX_DEFINE_OPERATIONS(8, 8) +VEC_MMX_DEFINE_OPERATIONS(16, 4) +VEC_MMX_DEFINE_OPERATIONS(32, 2) + +#undef VEC_MMX_DEFINE_OPERATIONS +#undef VEC_MMX_DEFINE_OPERATIONS_SIGN +#undef VEC_MMX_MUL_8x8 +#undef VEC_MMX_MUL_16x4 +#undef VEC_MMX_MUL_32x2 +#undef VEC_MMX_OPERATION_8x8 +#undef VEC_MMX_LSHIFT_8x8 +#undef VEC_MMX_LSHIFT_16x4 +#undef VEC_MMX_LSHIFT_32x2 +#undef VEC_MMX_RSHIFT_8x8 +#undef VEC_MMX_RSHIFT_16x4 +#undef VEC_MMX_RSHIFT_32x2 + +#endif /* VEC_IMPL_X86_MMX_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/x86/sse2.h Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,230 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_X86_SSE2_H_ +#define VEC_IMPL_X86_SSE2_H_ + +#define VEC_SSE2_OPERATION_8x16(op, sign) \ + do { \ + /* unpack and multiply */ \ + __m128i dst_even = _mm_##op##_epi16(vec1.sse, vec2.sse); \ + __m128i dst_odd = _mm_##op##_epi16(_mm_srli_epi16(vec1.sse, 8), _mm_srli_epi16(vec2.sse, 8)); \ + \ + /* repack */ \ + return (v##sign##int8x16){ .sse = _mm_or_si128( \ + _mm_slli_epi16(dst_odd, 8), \ + _mm_srli_epi16(_mm_slli_epi16(dst_even, 8), 8) \ + )}; \ + } while (0) + +// shifting +#define VEC_SSE2_LSHIFT_8x16(sign) \ + VEC_SSE2_OPERATION_8x16(sll, sign) + +#define VEC_SSE2_LSHIFT_16x8(sign) \ + do { \ + return (v##sign##int16x8){ .sse = _mm_sll_epi16(vec1.sse, vec2.sse) }; \ + } while (0) + +#define VEC_SSE2_LSHIFT_32x4(sign) \ + do { \ + return (v##sign##int32x4){ .sse = _mm_sll_epi32(vec1.sse, vec2.sse) }; \ + } while (0) + +#define VEC_SSE2_LSHIFT_64x2(sign) \ + do { \ + return (v##sign##int64x2){ .sse = _mm_sll_epi64(vec1.sse, vec2.sse) }; \ + } while (0) + +#define VEC_SSE2_RSHIFT_8x16(sign, aORl) \ + VEC_SSE2_OPERATION_8x16(sr##aORl, sign) + +#define VEC_SSE2_RSHIFT_16x8(sign, aORl) \ + do { \ + return (v##sign##int16x8){ .sse = _mm_sr##aORl##_epi16(vec1.sse, vec2.sse) }; \ + } while (0) + +#define VEC_SSE2_RSHIFT_32x4(sign, aORl) \ + do { \ + return (v##sign##int32x4){ .sse = _mm_sr##aORl##_epi32(vec1.sse, vec2.sse) }; \ + } while (0) + +#define VEC_SSE2_aRSHIFT_64x2(sign) \ + do { \ + return v##sign##int64x2_fallback_rshift(vec1, vec2); \ + } while (0) + +#define VEC_SSE2_lRSHIFT_64x2(sign) \ + do { \ + return (v##sign##int64x2){ .sse = _mm_srl_epi64(vec1.sse, vec2.sse) }; \ + } while (0) + +#define VEC_SSE2_RSHIFT_64x2(sign, aORl) \ + VEC_SSE2_##aORl##RSHIFT_64x2(sign) + +// shared between SSE2 variations +#define VEC_SSE2_MUL_8x16(sign) \ + VEC_SSE2_OPERATION_8x16(mullo, sign) + +#define VEC_SSE2_MUL_16x8(sign) \ + do { \ + /* we have a real instruction for this */ \ + return (v##sign##int16x8){ .sse = _mm_mullo_epi16(vec1.sse, vec2.sse) }; \ + } while (0) + +#define VEC_SSE2_MUL_32x4(sign) \ + do { \ + /* this was stolen from... somewhere :) */ \ + __m128i a13 = _mm_shuffle_epi32(vec1.sse, 0xF5); /* (-,a3,-,a1) */ \ + __m128i b13 = _mm_shuffle_epi32(vec2.sse, 0xF5); /* (-,b3,-,b1) */ \ + __m128i prod02 = _mm_mul_epu32(vec1.sse, vec2.sse); /* (-,a2*b2,-,a0*b0) */ \ + __m128i prod13 = _mm_mul_epu32(a13, b13); /* (-,a3*b3,-,a1*b1) */ \ + __m128i prod01 = _mm_unpacklo_epi32(prod02,prod13); /* (-,-,a1*b1,a0*b0) */ \ + __m128i prod23 = _mm_unpackhi_epi32(prod02,prod13); /* (-,-,a3*b3,a2*b2) */ \ + return (v##sign##int32x4) { .sse = _mm_unpacklo_epi64(prod01, prod23) }; /* (ab3,ab2,ab1,ab0) */ \ + } while (0) + +#define VEC_SSE2_MUL_64x2(sign) \ + do { \ + __m128i ac = _mm_mul_epu32(vec1.sse, vec2.sse); /* ac = (vec1 & UINT32_MAX) * (vec2 & UINT32_MAX); */ \ + __m128i b = _mm_srli_epi64(vec1.sse, 32); /* b = vec1 >> 32; */ \ + __m128i bc = _mm_mul_epu32(b, vec2.sse); /* bc = b * (vec2 & UINT32_MAX); */ \ + __m128i d = _mm_srli_epi64(vec2.sse, 32); /* d = vec2 >> 32; */ \ + __m128i ad = _mm_mul_epu32(vec1.sse, d); /* ad = (vec1 & UINT32_MAX) * d; */ \ + __m128i hi = _mm_add_epi64(bc, ad); /* hi = bc + ad; */ \ + hi = _mm_slli_epi64(hi, 32); /* hi <<= 32; */ \ + return (v##sign##int64x2) { .sse = _mm_add_epi64(hi, ac) }; /* return ac + hi; */ \ + } while (0) + +#define VEC_SSE2_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_load_aligned(const sign##int##bits##_t in[size]) \ + { \ + return (v##sign##int##bits##x##size) { .sse = _mm_load_si128((const __m128i *)in) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_load(const sign##int##bits##_t in[size]) \ + { \ + return (v##sign##int##bits##x##size) { .sse = _mm_loadu_si128((const __m128i *)in) }; \ + } \ + \ + static void v##sign##int##bits##x##size##_sse2_store_aligned(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + _mm_store_si128((__m128i *)out, vec.sse); \ + } \ + \ + static void v##sign##int##bits##x##size##_sse2_store(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + _mm_storeu_si128((__m128i *)out, vec.sse); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .sse = _mm_add_epi##bits(vec1.sse, vec2.sse) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .sse = _mm_sub_epi##bits(vec1.sse, vec2.sse) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_SSE2_MUL_##bits##x##size(sign); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .sse = _mm_and_si128(vec1.sse, vec2.sse) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .sse = _mm_or_si128(vec1.sse, vec2.sse) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return (v##sign##int##bits##x##size) { .sse = _mm_xor_si128(vec1.sse, vec2.sse) }; \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_SSE2_LSHIFT_##bits##x##size(sign); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_SSE2_RSHIFT_##bits##x##size(sign, a); \ + } \ + \ + static v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_SSE2_RSHIFT_##bits##x##size(sign, l); \ + } \ + \ + static v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_sse2 = { \ + .load_aligned = v##sign##int##bits##x##size##_sse2_load_aligned, \ + .load = v##sign##int##bits##x##size##_sse2_load, \ + .store_aligned = v##sign##int##bits##x##size##_sse2_store_aligned, \ + .store = v##sign##int##bits##x##size##_sse2_store, \ + .add = v##sign##int##bits##x##size##_sse2_add, \ + .sub = v##sign##int##bits##x##size##_sse2_sub, \ + .mul = v##sign##int##bits##x##size##_sse2_mul, \ + .and = v##sign##int##bits##x##size##_sse2_and, \ + .or = v##sign##int##bits##x##size##_sse2_or, \ + .xor = v##sign##int##bits##x##size##_sse2_xor, \ + .lshift = v##sign##int##bits##x##size##_sse2_lshift, \ + .rshift = v##sign##int##bits##x##size##_sse2_rshift, \ + .lrshift = v##sign##int##bits##x##size##_sse2_lrshift, \ + }; + +#define VEC_SSE2_DEFINE_OPERATIONS(bits, size) \ + VEC_SSE2_DEFINE_OPERATIONS_SIGN( , bits, size) \ + VEC_SSE2_DEFINE_OPERATIONS_SIGN(u, bits, size) + +// SSE is *only* 128-bit +VEC_SSE2_DEFINE_OPERATIONS(8, 16) +VEC_SSE2_DEFINE_OPERATIONS(16, 8) +VEC_SSE2_DEFINE_OPERATIONS(32, 4) +VEC_SSE2_DEFINE_OPERATIONS(64, 2) + +#undef VEC_SSE2_DEFINE_OPERATIONS +#undef VEC_SSE2_DEFINE_OPERATIONS_SIGN +#undef VEC_SSE2_MUL_8x16 +#undef VEC_SSE2_MUL_16x8 +#undef VEC_SSE2_MUL_32x4 +#undef VEC_SSE2_MUL_64x2 +#undef VEC_SSE2_OPERATION_8x16 +#undef VEC_SSE2_LSHIFT_8x16 +#undef VEC_SSE2_LSHIFT_16x8 +#undef VEC_SSE2_LSHIFT_32x4 +#undef VEC_SSE2_LSHIFT_64x2 +#undef VEC_SSE2_RSHIFT_8x16 +#undef VEC_SSE2_RSHIFT_16x8 +#undef VEC_SSE2_RSHIFT_32x4 +#undef VEC_SSE2_aRSHIFT_64x2 +#undef VEC_SSE2_lRSHIFT_64x2 +#undef VEC_SSE2_RSHIFT_64x2 + +#endif /* VEC_IMPL_X86_SSE2_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/x86/sse41.h Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,55 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_X86_SSE41_H_ +#define VEC_IMPL_X86_SSE41_H_ + +#define VEC_SSE41_DEFINE_OPERATIONS(sign) \ + static v##sign##int32x4 v##sign##int32x4_sse41_mul(v##sign##int32x4 vec1, v##sign##int32x4 vec2) \ + { \ + return (v##sign##int32x4){ .sse = _mm_mullo_epi32(vec1.sse, vec2.sse) }; \ + } \ + \ + static v##sign##int32x4_impl v##sign##int32x4_impl_sse41 = { \ + .load_aligned = v##sign##int32x4_sse2_load_aligned, \ + .load = v##sign##int32x4_sse2_load, \ + .store_aligned = v##sign##int32x4_sse2_store_aligned, \ + .store = v##sign##int32x4_sse2_store, \ + .add = v##sign##int32x4_sse2_add, \ + .sub = v##sign##int32x4_sse2_sub, \ + .mul = v##sign##int32x4_sse41_mul, \ + .and = v##sign##int32x4_sse2_and, \ + .or = v##sign##int32x4_sse2_or, \ + .xor = v##sign##int32x4_sse2_xor, \ + .lshift = v##sign##int32x4_sse2_lshift, \ + .rshift = v##sign##int32x4_sse2_rshift, \ + .lrshift = v##sign##int32x4_sse2_lrshift, \ + }; + +VEC_SSE41_DEFINE_OPERATIONS() +VEC_SSE41_DEFINE_OPERATIONS(u) + +#undef VEC_SSE41_DEFINE_OPERATIONS + +#endif /* VEC_IMPL_X86_SSE41_H_ */
--- a/include/vec/vec.h Tue Nov 19 15:55:01 2024 -0500 +++ b/include/vec/vec.h Wed Nov 20 04:10:37 2024 -0500 @@ -29,6 +29,10 @@ #include <string.h> #include <limits.h> +#define VEC_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define VEC_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define VEC_CLAMP(x, min, max) (VEC_MIN(VEC_MAX((x), (min)), (max))) + #define VEC_SEMVER_ATLEAST(a, b, c, x, y, z) \ (((a) >= (x)) && \ ((a) > x || (b) >= (y)) && \ @@ -39,14 +43,11 @@ /* GCC/clang attributes */ #if defined(__has_attribute) -# if __has_attribute(__always_inline__) -# define VEC_ALWAYS_INLINE __attribute__((__always_inline__)) -# endif # if __has_attribute(__aligned__) # define VEC_ALIGNED(x) __attribute__((__aligned__(x))) # endif # if __has_attribute(__vector_size__) -# define VEC_HAVE_GNUC_VECTORS +# define VEC_COMPILER_HAS_GNUC_VECTORS # endif #endif @@ -56,26 +57,6 @@ # endif #endif -/* FIXME: gcc 4.2 on Mac OS X doesn't have always_inline, - * even though docs and many online sources say that it - * should have it. */ - -#ifndef VEC_ALWAYS_INLINE -# define VEC_ALWAYS_INLINE -#endif - -/* Allow users to define all of the symbols externally in - * one translation unit, or as a shared library. */ -#ifdef VEC_EXTERN -# ifdef VEC_EXTERN_DEFINE -# define VEC_FUNC_KEYWORDS extern inline -# else -# define VEC_FUNC_KEYWORDS inline -# endif -#else -# define VEC_FUNC_KEYWORDS static inline VEC_ALWAYS_INLINE -#endif - #if (__STDC_VERSION__ >= 201112L) # define VEC_STATIC_ASSERT(x, msg) _Static_assert(x, msg) #else @@ -97,131 +78,226 @@ /* --------------------------------------------------------------- */ /* Detect compiler SIMD support */ +#define VEC_GENERIC_ALIGNMENT 1 +#define VEC_ALTIVEC_ALIGNMENT 16 +#define VEC_SSE2_ALIGNMENT 16 +#define VEC_AVX2_ALIGNMENT 32 +#define VEC_AVX512F_ALIGNMENT 64 + +// for the generic implementation, 64-bit +#define VINT8x8_ALIGNMENT VEC_GENERIC_ALIGNMENT +#define VINT16x4_ALIGNMENT VEC_GENERIC_ALIGNMENT +#define VINT32x2_ALIGNMENT VEC_GENERIC_ALIGNMENT +#define VUINT8x8_ALIGNMENT VEC_GENERIC_ALIGNMENT +#define VUINT16x4_ALIGNMENT VEC_GENERIC_ALIGNMENT +#define VUINT32x2_ALIGNMENT VEC_GENERIC_ALIGNMENT + +#define VINT8x16_ALIGNMENT VINT8x8_ALIGNMENT +#define VINT16x8_ALIGNMENT VINT16x4_ALIGNMENT +#define VINT32x4_ALIGNMENT VINT32x2_ALIGNMENT +#define VINT64x2_ALIGNMENT VEC_GENERIC_ALIGNMENT +#define VUINT8x16_ALIGNMENT VUINT8x8_ALIGNMENT +#define VUINT16x8_ALIGNMENT VUINT16x4_ALIGNMENT +#define VUINT32x4_ALIGNMENT VUINT32x2_ALIGNMENT +#define VUINT64x2_ALIGNMENT VEC_GENERIC_ALIGNMENT + +#define VINT8x32_ALIGNMENT VINT8x16_ALIGNMENT +#define VINT16x16_ALIGNMENT VINT16x8_ALIGNMENT +#define VINT32x8_ALIGNMENT VINT32x4_ALIGNMENT +#define VINT64x4_ALIGNMENT VINT64x2_ALIGNMENT +#define VUINT8x32_ALIGNMENT VUINT8x16_ALIGNMENT +#define VUINT16x16_ALIGNMENT VUINT16x8_ALIGNMENT +#define VUINT32x8_ALIGNMENT VUINT32x4_ALIGNMENT +#define VUINT64x4_ALIGNMENT VUINT64x2_ALIGNMENT + +#define VINT8x64_ALIGNMENT VINT8x32_ALIGNMENT +#define VINT16x32_ALIGNMENT VINT16x16_ALIGNMENT +#define VINT32x16_ALIGNMENT VINT32x8_ALIGNMENT +#define VINT64x8_ALIGNMENT VINT64x4_ALIGNMENT +#define VUINT8x64_ALIGNMENT VUINT8x32_ALIGNMENT +#define VUINT16x32_ALIGNMENT VUINT16x16_ALIGNMENT +#define VUINT32x16_ALIGNMENT VUINT32x8_ALIGNMENT +#define VUINT64x8_ALIGNMENT VUINT64x4_ALIGNMENT + +#ifndef VEC_SUPPRESS_HW + // IIRC `__VEC__' is also defined, but I don't know for sure. // IBM says that `__ALTIVEC__' is standard though. #ifdef __ALTIVEC__ # include <altivec.h> # define VEC_COMPILER_HAS_ALTIVEC +# if defined(__POWER8__) && defined(__VSX__) +# define VEC_COMPILER_HAS_ALTIVEC_VSX +# endif +# if VINT8x16_ALIGNMENT < VEC_ALTIVEC_ALIGNMENT +# undef VINT8x16_ALIGNMENT +# define VINT8x16_ALIGNMENT VEC_ALTIVEC_ALIGNMENT +# endif +# if VINT16x8_ALIGNMENT < VEC_ALTIVEC_ALIGNMENT +# undef VINT16x8_ALIGNMENT +# define VINT16x8_ALIGNMENT VEC_ALTIVEC_ALIGNMENT +# endif +# if VINT32x4_ALIGNMENT < VEC_ALTIVEC_ALIGNMENT +# undef VINT32x4_ALIGNMENT +# define VINT32x4_ALIGNMENT VEC_ALTIVEC_ALIGNMENT +# endif +# if VINT64x2_ALIGNMENT < VEC_ALTIVEC_ALIGNMENT +# undef VINT64x2_ALIGNMENT +# define VINT64x2_ALIGNMENT VEC_ALTIVEC_ALIGNMENT +# endif +# if VUINT8x16_ALIGNMENT < VEC_ALTIVEC_ALIGNMENT +# undef VUINT8x16_ALIGNMENT +# define VUINT8x16_ALIGNMENT VEC_ALTIVEC_ALIGNMENT +# endif +# if VUINT16x8_ALIGNMENT < VEC_ALTIVEC_ALIGNMENT +# undef VUINT16x8_ALIGNMENT +# define VUINT16x8_ALIGNMENT VEC_ALTIVEC_ALIGNMENT +# endif +# if VUINT32x4_ALIGNMENT < VEC_ALTIVEC_ALIGNMENT +# undef VUINT32x4_ALIGNMENT +# define VUINT32x4_ALIGNMENT VEC_ALTIVEC_ALIGNMENT +# endif +# if VUINT64x2_ALIGNMENT < VEC_ALTIVEC_ALIGNMENT +# undef VUINT64x2_ALIGNMENT +# define VUINT64x2_ALIGNMENT VEC_ALTIVEC_ALIGNMENT +# endif +#endif -# define VINT8x16_ALIGNMENT 16 -# define VINT16x8_ALIGNMENT 16 -# define VINT32x4_ALIGNMENT 16 -# define VINT64x2_ALIGNMENT 16 +#ifdef __MMX__ +# include <mmintrin.h> +# define VEC_COMPILER_HAS_MMX #endif #ifdef __SSE2__ -# include <immintrin.h> +# include <emmintrin.h> # define VEC_COMPILER_HAS_SSE2 -# ifdef __SSE42__ -# define VEC_COMPILER_HAS_SSE42 +# ifdef __SSE4_1__ +# define VEC_COMPILER_HAS_SSE41 +# endif +# if VINT8x16_ALIGNMENT < VEC_SSE2_ALIGNMENT +# undef VINT8x16_ALIGNMENT +# define VINT8x16_ALIGNMENT VEC_SSE2_ALIGNMENT +# endif +# if VINT16x8_ALIGNMENT < VEC_SSE2_ALIGNMENT +# undef VINT16x8_ALIGNMENT +# define VINT16x8_ALIGNMENT VEC_SSE2_ALIGNMENT +# endif +# if VINT32x4_ALIGNMENT < VEC_SSE2_ALIGNMENT +# undef VINT32x4_ALIGNMENT +# define VINT32x4_ALIGNMENT VEC_SSE2_ALIGNMENT # endif - -# define VINT8x16_ALIGNMENT 16 -# define VINT16x8_ALIGNMENT 16 -# define VINT32x4_ALIGNMENT 16 -# define VINT64x2_ALIGNMENT 16 -#endif - -#ifndef VINT8x16_ALIGNMENT -# define VINT8x16_ALIGNMENT 1 -#endif -#ifndef VINT16x8_ALIGNMENT -# define VINT16x8_ALIGNMENT 1 -#endif -#ifndef VINT32x4_ALIGNMENT -# define VINT32x4_ALIGNMENT 1 -#endif -#ifndef VINT64x2_ALIGNMENT -# define VINT64x2_ALIGNMENT 1 -#endif -#ifndef VUINT8x16_ALIGNMENT -# define VUINT8x16_ALIGNMENT 1 -#endif -#ifndef VUINT16x8_ALIGNMENT -# define VUINT16x8_ALIGNMENT 1 -#endif -#ifndef VUINT32x4_ALIGNMENT -# define VUINT32x4_ALIGNMENT 1 -#endif -#ifndef VUINT64x2_ALIGNMENT -# define VUINT64x2_ALIGNMENT 1 +# if VINT64x2_ALIGNMENT < VEC_SSE2_ALIGNMENT +# undef VINT64x2_ALIGNMENT +# define VINT64x2_ALIGNMENT VEC_SSE2_ALIGNMENT +# endif +# if VUINT8x16_ALIGNMENT < VEC_SSE2_ALIGNMENT +# undef VUINT8x16_ALIGNMENT +# define VUINT8x16_ALIGNMENT VEC_SSE2_ALIGNMENT +# endif +# if VUINT16x8_ALIGNMENT < VEC_SSE2_ALIGNMENT +# undef VUINT16x8_ALIGNMENT +# define VUINT16x8_ALIGNMENT VEC_SSE2_ALIGNMENT +# endif +# if VUINT32x4_ALIGNMENT < VEC_SSE2_ALIGNMENT +# undef VUINT32x4_ALIGNMENT +# define VUINT32x4_ALIGNMENT VEC_SSE2_ALIGNMENT +# endif +# if VUINT64x2_ALIGNMENT < VEC_SSE2_ALIGNMENT +# undef VUINT64x2_ALIGNMENT +# define VUINT64x2_ALIGNMENT VEC_SSE2_ALIGNMENT +# endif #endif -// generic 256-bit is just doubled 128-bit -#ifndef VINT8x32_ALIGNMENT -# define VINT8x32_ALIGNMENT VINT8x16_ALIGNMENT -#endif -#ifndef VINT16x16_ALIGNMENT -# define VINT16x16_ALIGNMENT VINT16x8_ALIGNMENT -#endif -#ifndef VINT32x8_ALIGNMENT -# define VINT32x8_ALIGNMENT VINT32x4_ALIGNMENT -#endif -#ifndef VINT64x4_ALIGNMENT -# define VINT64x4_ALIGNMENT VINT64x2_ALIGNMENT -#endif -#ifndef VUINT8x32_ALIGNMENT -# define VUINT8x32_ALIGNMENT VUINT8x16_ALIGNMENT -#endif -#ifndef VUINT16x16_ALIGNMENT -# define VUINT16x16_ALIGNMENT VUINT16x8_ALIGNMENT -#endif -#ifndef VUINT32x8_ALIGNMENT -# define VUINT32x8_ALIGNMENT VUINT32x4_ALIGNMENT -#endif -#ifndef VUINT64x4_ALIGNMENT -# define VUINT64x4_ALIGNMENT VUINT64x2_ALIGNMENT +#ifdef __AVX2__ +# include <immintrin.h> +# define VEC_COMPILER_HAS_AVX2 +# if VINT8x32_ALIGNMENT < VEC_AVX2_ALIGNMENT +# undef VINT8x32_ALIGNMENT +# define VINT8x32_ALIGNMENT VEC_AVX2_ALIGNMENT +# endif +# if VINT16x16_ALIGNMENT < VEC_AVX2_ALIGNMENT +# undef VINT16x16_ALIGNMENT +# define VINT16x16_ALIGNMENT VEC_AVX2_ALIGNMENT +# endif +# if VINT32x8_ALIGNMENT < VEC_AVX2_ALIGNMENT +# undef VINT32x8_ALIGNMENT +# define VINT32x8_ALIGNMENT VEC_AVX2_ALIGNMENT +# endif +# if VINT64x4_ALIGNMENT < VEC_AVX2_ALIGNMENT +# undef VINT64x4_ALIGNMENT +# define VINT64x4_ALIGNMENT VEC_AVX2_ALIGNMENT +# endif +# if VUINT8x32_ALIGNMENT < VEC_AVX2_ALIGNMENT +# undef VUINT8x32_ALIGNMENT +# define VUINT8x32_ALIGNMENT VEC_AVX2_ALIGNMENT +# endif +# if VUINT16x16_ALIGNMENT < VEC_AVX2_ALIGNMENT +# undef VUINT16x16_ALIGNMENT +# define VUINT16x16_ALIGNMENT VEC_AVX2_ALIGNMENT +# endif +# if VUINT32x8_ALIGNMENT < VEC_AVX2_ALIGNMENT +# undef VUINT32x8_ALIGNMENT +# define VUINT32x8_ALIGNMENT VEC_AVX2_ALIGNMENT +# endif +# if VUINT64x4_ALIGNMENT < VEC_AVX2_ALIGNMENT +# undef VUINT64x4_ALIGNMENT +# define VUINT64x4_ALIGNMENT VEC_AVX2_ALIGNMENT +# endif #endif -// generic 512-bit is just doubled 256-bit -#ifndef VINT8x64_ALIGNMENT -# define VINT8x64_ALIGNMENT VINT8x32_ALIGNMENT -#endif -#ifndef VINT16x32_ALIGNMENT -# define VINT16x32_ALIGNMENT VINT16x16_ALIGNMENT -#endif -#ifndef VINT32x16_ALIGNMENT -# define VINT32x16_ALIGNMENT VINT32x8_ALIGNMENT -#endif -#ifndef VINT64x8_ALIGNMENT -# define VINT64x8_ALIGNMENT VINT64x4_ALIGNMENT -#endif -#ifndef VUINT8x64_ALIGNMENT -# define VUINT8x64_ALIGNMENT VUINT8x32_ALIGNMENT -#endif -#ifndef VUINT16x32_ALIGNMENT -# define VUINT16x32_ALIGNMENT VUINT16x16_ALIGNMENT -#endif -#ifndef VUINT32x16_ALIGNMENT -# define VUINT32x16_ALIGNMENT VUINT32x16_ALIGNMENT -#endif -#ifndef VUINT64x8_ALIGNMENT -# define VUINT64x8_ALIGNMENT VUINT64x4_ALIGNMENT +#ifdef __AVX512F__ +# include <immintrin.h> +# define VEC_COMPILER_HAS_AVX512F +# if VINT8x64_ALIGNMENT < VEC_AVX512F_ALIGNMENT +# undef VINT8x64_ALIGNMENT +# define VINT8x64_ALIGNMENT VEC_AVX512F_ALIGNMENT +# endif +# if VINT16x32_ALIGNMENT < VEC_AVX512F_ALIGNMENT +# undef VINT16x32_ALIGNMENT +# define VINT16x32_ALIGNMENT VEC_AVX512F_ALIGNMENT +# endif +# if VINT32x16_ALIGNMENT < VEC_AVX512F_ALIGNMENT +# undef VINT32x16_ALIGNMENT +# define VINT32x16_ALIGNMENT VEC_AVX512F_ALIGNMENT +# endif +# if VINT64x8_ALIGNMENT < VEC_AVX512F_ALIGNMENT +# undef VINT64x8_ALIGNMENT +# define VINT64x8_ALIGNMENT VEC_AVX512F_ALIGNMENT +# endif +# if VUINT8x64_ALIGNMENT < VEC_AVX512F_ALIGNMENT +# undef VUINT8x64_ALIGNMENT +# define VUINT8x64_ALIGNMENT VEC_AVX512F_ALIGNMENT +# endif +# if VUINT16x32_ALIGNMENT < VEC_AVX512F_ALIGNMENT +# undef VUINT16x32_ALIGNMENT +# define VUINT16x32_ALIGNMENT VEC_AVX512F_ALIGNMENT +# endif +# if VUINT32x16_ALIGNMENT < VEC_AVX512F_ALIGNMENT +# undef VUINT32x16_ALIGNMENT +# define VUINT32x16_ALIGNMENT VEC_AVX512F_ALIGNMENT +# endif +# if VUINT64x8_ALIGNMENT < VEC_AVX512F_ALIGNMENT +# undef VUINT64x8_ALIGNMENT +# define VUINT64x8_ALIGNMENT VEC_AVX512F_ALIGNMENT +# endif #endif -/* --------------------------------------------------------------- */ -/* Detect CPU SIMD support */ - -// stubs for now... will be implemented sometime -#define VEC_CPU_have_SSE2() (0) -#define VEC_CPU_have_SSE42() (0) -#define VEC_CPU_have_ALTIVEC() (0) -#define VEC_CPU_have_ALTIVEC_VSX() (0) +#endif /* --------------------------------------------------------------- */ /* bit shift */ -VEC_FUNC_KEYWORDS uintmax_t vec_ulrshift(uintmax_t x, unsigned int y) +inline uintmax_t vec_ulrshift(uintmax_t x, unsigned int y) { return x >> y; } -VEC_FUNC_KEYWORDS uintmax_t vec_ullshift(uintmax_t x, unsigned int y) +inline uintmax_t vec_ullshift(uintmax_t x, unsigned int y) { return x << y; } -VEC_FUNC_KEYWORDS intmax_t vec_lrshift(intmax_t x, unsigned int y) +inline intmax_t vec_lrshift(intmax_t x, unsigned int y) { // reinterpret as unsigned integer and then shift union { @@ -234,7 +310,7 @@ return xx.d; } -VEC_FUNC_KEYWORDS intmax_t vec_llshift(intmax_t x, unsigned int y) +inline intmax_t vec_llshift(intmax_t x, unsigned int y) { // reinterpret as unsigned integer and then shift union { @@ -247,12 +323,12 @@ return xx.d; } -VEC_FUNC_KEYWORDS uintmax_t vec_urshift(uintmax_t x, unsigned int y) +inline uintmax_t vec_urshift(uintmax_t x, unsigned int y) { return x >> y; } -VEC_FUNC_KEYWORDS uintmax_t vec_ulshift(uintmax_t x, unsigned int y) +inline uintmax_t vec_ulshift(uintmax_t x, unsigned int y) { return x << y; } @@ -283,7 +359,7 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. **/ -VEC_FUNC_KEYWORDS intmax_t vec_rshift(intmax_t x, unsigned int y) +inline intmax_t vec_rshift(intmax_t x, unsigned int y) { static const uintmax_t roffset = ((uintmax_t)1) << ((sizeof(intmax_t) * CHAR_BIT) - 1); @@ -302,7 +378,7 @@ return xx.d; } -VEC_FUNC_KEYWORDS intmax_t vec_lshift(intmax_t x, unsigned int y) +inline intmax_t vec_lshift(intmax_t x, unsigned int y) { static const uintmax_t roffset = ((uintmax_t)1) << ((sizeof(intmax_t) * CHAR_BIT) - 1); @@ -320,110 +396,246 @@ return xx.d; } +#ifdef VEC_IMPLEMENTATION +extern inline uintmax_t vec_ulrshift(uintmax_t x, unsigned int y); +extern inline uintmax_t vec_ullshift(uintmax_t x, unsigned int y); +extern inline intmax_t vec_lrshift(intmax_t x, unsigned int y); +extern inline intmax_t vec_llshift(intmax_t x, unsigned int y); +extern inline uintmax_t vec_urshift(uintmax_t x, unsigned int y); +extern inline uintmax_t vec_ulshift(uintmax_t x, unsigned int y); +extern inline intmax_t vec_rshift(intmax_t x, unsigned int y); +extern inline intmax_t vec_lshift(intmax_t x, unsigned int y); +#endif + /* --------------------------------------------------------------- */ /* Array alignment macros */ -#include <stdio.h> - +/* the alignment must be specified in bytes and must be a multiple of the + * type size. it is always assumed that the type will be on a boundary of + * its size, which may or may not be true */ #ifdef VEC_ALIGNED # define VEC_ALIGNED_ARRAY(type, var, length, align) \ VEC_ALIGNED(align) type var[length] # define VEC_ALIGNED_ARRAY_SIZEOF(var, align) \ (sizeof(var)) #else -/* the alignment must be specified in bytes and must be a multiple of the - * type size. it is always assumed that the type will be on a boundary of - * its size, which may or may not be true */ # define VEC_ALIGNED_ARRAY(type, var, length, align) \ - VEC_STATIC_ASSERT(align % sizeof(type) == 0 && align != 0, "vec: alignment needs to be a multiple of the type size and non-zero"); \ - type vec_##var##_unaligned_[(length) + (align / sizeof(type)) - 1]; \ + VEC_STATIC_ASSERT(align && ((align & (align - 1)) == 0), "vec: alignment must be a power of two"); \ + type vec_##var##_unaligned_[(length) + (align / sizeof(type))]; \ type *var = (type *)(((uintptr_t)vec_##var##_unaligned_ + (align - 1)) & ~(align - 1)); \ VEC_ASSERT(((uintptr_t)var) % align == 0, "vec: VEC_ALIGNED_ARRAY result is actually not aligned") # define VEC_ALIGNED_ARRAY_SIZEOF(var, align) \ - (sizeof(vec_##var##_unaligned_) - ((align) - 1)) + (sizeof(vec_##var##_unaligned_) - (align - 1)) #endif -#define VEC_ALIGNED_ARRAY_LENGTH(var, align) \ - (VEC_ALIGNED_ARRAY_SIZEOF(var, align)/sizeof(*var)) +#define VEC_ALIGNED_ARRAY_LENGTH(var) \ + (VEC_ALIGNED_ARRAY_SIZEOF(var)/sizeof(*var)) // ------------------------------------------------------------ // predefined variants for each vector type +#define VINT8x8_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int8_t, var, 8, VINT8x8_ALIGNMENT) +#define VINT8x8_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT8x8_ALIGNMENT) +#define VINT8x8_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT8x8_ALIGNMENT) +#define VINT8x8_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT8x8_ALIGNMENT == 0) + +#define VINT16x4_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int16_t, var, 4, VINT16x4_ALIGNMENT) +#define VINT16x4_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT16x4_ALIGNMENT) +#define VINT16x4_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT16x4_ALIGNMENT) +#define VINT16x4_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT16x4_ALIGNMENT == 0) + +#define VINT32x2_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int32_t, var, 2, VINT32x2_ALIGNMENT) +#define VINT32x2_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT32x2_ALIGNMENT) +#define VINT32x2_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT32x2_ALIGNMENT) +#define VINT32x2_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT32x2_ALIGNMENT == 0) + +#define VUINT8x8_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint8_t, var, 8, VUINT8x8_ALIGNMENT) +#define VUINT8x8_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT8x8_ALIGNMENT) +#define VUINT8x8_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT8x8_ALIGNMENT) +#define VUINT8x8_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT8x8_ALIGNMENT == 0) + +#define VUINT16x4_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint16_t, var, 4, VUINT16x4_ALIGNMENT) +#define VUINT16x4_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT16x4_ALIGNMENT) +#define VUINT16x4_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT16x4_ALIGNMENT) +#define VUINT16x4_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT16x4_ALIGNMENT == 0) + +#define VUINT32x2_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint32_t, var, 2, VUINT32x2_ALIGNMENT) +#define VUINT32x2_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT32x2_ALIGNMENT) +#define VUINT32x2_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT32x2_ALIGNMENT) +#define VUINT32x2_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT32x2_ALIGNMENT == 0) + #define VINT8x16_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int8_t, var, 16, VINT8x16_ALIGNMENT) +#define VINT8x16_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT8x16_ALIGNMENT) +#define VINT8x16_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT8x16_ALIGNMENT) #define VINT8x16_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT8x16_ALIGNMENT == 0) #define VINT16x8_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int16_t, var, 8, VINT16x8_ALIGNMENT) +#define VINT16x8_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT16x8_ALIGNMENT) +#define VINT16x8_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT16x8_ALIGNMENT) #define VINT16x8_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT16x8_ALIGNMENT == 0) #define VINT32x4_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int32_t, var, 4, VINT32x4_ALIGNMENT) +#define VINT32x4_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT32x4_ALIGNMENT) +#define VINT32x4_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT32x4_ALIGNMENT) #define VINT32x4_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT32x4_ALIGNMENT == 0) #define VINT64x2_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int64_t, var, 2, VINT64x2_ALIGNMENT) +#define VINT64x2_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT64x2_ALIGNMENT) +#define VINT64x2_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT64x2_ALIGNMENT) #define VINT64x2_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT64x2_ALIGNMENT == 0) #define VUINT8x16_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint8_t, var, 16, VUINT8x16_ALIGNMENT) +#define VUINT8x16_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT8x16_ALIGNMENT) +#define VUINT8x16_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT8x16_ALIGNMENT) #define VUINT8x16_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT8x16_ALIGNMENT == 0) #define VUINT16x8_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint16_t, var, 8, VUINT16x8_ALIGNMENT) +#define VUINT16x8_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT16x8_ALIGNMENT) +#define VUINT16x8_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT16x8_ALIGNMENT) #define VUINT16x8_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT16x8_ALIGNMENT == 0) #define VUINT32x4_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint32_t, var, 4, VUINT32x4_ALIGNMENT) +#define VUINT32x4_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT32x4_ALIGNMENT) +#define VUINT32x4_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT32x4_ALIGNMENT) #define VUINT32x4_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT32x4_ALIGNMENT == 0) #define VUINT64x2_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint64_t, var, 2, VUINT64x2_ALIGNMENT) +#define VUINT64x2_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT64x2_ALIGNMENT) +#define VUINT64x2_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT64x2_ALIGNMENT) #define VUINT64x2_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT64x2_ALIGNMENT == 0) #define VINT8x32_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int8_t, var, 32, VINT8x32_ALIGNMENT) +#define VINT8x32_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT8x32_ALIGNMENT) +#define VINT8x32_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT8x32_ALIGNMENT) #define VINT8x32_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT8x32_ALIGNMENT == 0) #define VINT16x16_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int16_t, var, 16, VINT16x16_ALIGNMENT) +#define VINT16x16_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT16x16_ALIGNMENT) +#define VINT16x16_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT16x16_ALIGNMENT) #define VINT16x16_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT16x16_ALIGNMENT == 0) #define VINT32x8_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int32_t, var, 8, VINT32x8_ALIGNMENT) +#define VINT32x8_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT32x8_ALIGNMENT) +#define VINT32x8_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT32x8_ALIGNMENT) #define VINT32x8_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT32x8_ALIGNMENT == 0) #define VINT64x4_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int64_t, var, 4, VINT64x4_ALIGNMENT) +#define VINT64x4_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT64x4_ALIGNMENT) +#define VINT64x4_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT64x4_ALIGNMENT) #define VINT64x4_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT64x4_ALIGNMENT == 0) #define VUINT8x32_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint8_t, var, 32, VUINT8x32_ALIGNMENT) +#define VUINT8x32_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT8x32_ALIGNMENT) +#define VUINT8x32_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT8x32_ALIGNMENT) #define VUINT8x32_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT8x32_ALIGNMENT == 0) #define VUINT16x16_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint16_t, var, 16, VUINT16x16_ALIGNMENT) +#define VUINT16x16_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT16x16_ALIGNMENT) +#define VUINT16x16_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT16x16_ALIGNMENT) #define VUINT16x16_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT16x16_ALIGNMENT == 0) #define VUINT32x8_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint32_t, var, 8, VUINT32x8_ALIGNMENT) +#define VUINT32x8_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT32x8_ALIGNMENT) +#define VUINT32x8_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT32x8_ALIGNMENT) #define VUINT32x8_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT32x8_ALIGNMENT == 0) #define VUINT64x4_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint64_t, var, 4, VUINT64x4_ALIGNMENT) +#define VUINT64x4_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT64x4_ALIGNMENT) +#define VUINT64x4_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT64x4_ALIGNMENT) #define VUINT64x4_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT64x4_ALIGNMENT == 0) #define VINT8x64_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int8_t, var, 64, VINT8x64_ALIGNMENT) +#define VINT8x64_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT8x64_ALIGNMENT) +#define VINT8x64_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT8x64_ALIGNMENT) #define VINT8x64_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT8x64_ALIGNMENT == 0) -#define VINT16x32_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int16_t, var, 32, VINT16x16_ALIGNMENT) +#define VINT16x32_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int16_t, var, 32, VINT16x32_ALIGNMENT) +#define VINT16x32_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT16x32_ALIGNMENT) +#define VINT16x32_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT16x32_ALIGNMENT) #define VINT16x32_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT16x16_ALIGNMENT == 0) #define VINT32x16_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int32_t, var, 16, VINT32x16_ALIGNMENT) +#define VINT32x16_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT32x16_ALIGNMENT) +#define VINT32x16_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT32x16_ALIGNMENT) #define VINT32x16_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT32x16_ALIGNMENT == 0) #define VINT64x8_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(int64_t, var, 8, VINT64x8_ALIGNMENT) +#define VINT64x8_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VINT64x8_ALIGNMENT) +#define VINT64x8_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VINT64x8_ALIGNMENT) #define VINT64x8_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VINT64x8_ALIGNMENT == 0) #define VUINT8x64_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint8_t, var, 64, VUINT8x64_ALIGNMENT) +#define VUINT8x64_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT8x64_ALIGNMENT) +#define VUINT8x64_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT8x64_ALIGNMENT) #define VUINT8x64_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT8x64_ALIGNMENT == 0) -#define VUINT16x32_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint16_t, var, 32, VUINT16x16_ALIGNMENT) +#define VUINT16x32_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint16_t, var, 32, VUINT16x32_ALIGNMENT) +#define VUINT16x32_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT16x32_ALIGNMENT) +#define VUINT16x32_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT16x32_ALIGNMENT) #define VUINT16x32_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT16x16_ALIGNMENT == 0) #define VUINT32x16_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint32_t, var, 16, VUINT32x16_ALIGNMENT) +#define VUINT32x16_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT32x16_ALIGNMENT) +#define VUINT32x16_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT32x16_ALIGNMENT) #define VUINT32x16_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT32x16_ALIGNMENT == 0) #define VUINT64x8_ALIGNED_ARRAY(var) VEC_ALIGNED_ARRAY(uint64_t, var, 8, VUINT64x8_ALIGNMENT) +#define VUINT64x8_ALIGNED_ARRAY_SIZEOF(var) VEC_ALIGNED_ARRAY_SIZEOF(var, VUINT64x8_ALIGNMENT) +#define VUINT64x8_ALIGNED_ARRAY_LENGTH(var) VEC_ALIGNED_ARRAY_LENGTH(var, VUINT64x8_ALIGNMENT) #define VUINT64x8_PTR_ALIGNED(ptr) (((uintptr_t)ptr) % VUINT64x8_ALIGNMENT == 0) /* --------------------------------------------------------------- */ /* Defines the structures for each vector type */ +// 64-bit +typedef union { +#ifdef VEC_COMPILER_HAS_MMX + __m64 mmx; +#endif + + uint8_t generic[8]; +} vuint8x8; + +typedef union { +#ifdef VEC_COMPILER_HAS_MMX + __m64 mmx; +#endif + + uint16_t generic[4]; +} vuint16x4; + +typedef union { +#ifdef VEC_COMPILER_HAS_MMX + __m64 mmx; +#endif + + uint32_t generic[2]; +} vuint32x2; + +typedef union { +#ifdef VEC_COMPILER_HAS_MMX + __m64 mmx; +#endif + + int8_t generic[8]; +} vint8x8; + +typedef union { +#ifdef VEC_COMPILER_HAS_MMX + __m64 mmx; +#endif + + int16_t generic[4]; +} vint16x4; + +typedef union { +#ifdef VEC_COMPILER_HAS_MMX + __m64 mmx; +#endif + + int32_t generic[2]; +} vint32x2; + // 128-bit typedef union { #ifdef VEC_COMPILER_HAS_SSE2 @@ -432,7 +644,7 @@ #ifdef VEC_COMPILER_HAS_ALTIVEC vector unsigned char altivec; #endif - uint8_t generic[16]; + vuint8x8 generic[2]; } vuint8x16; typedef union { @@ -442,7 +654,7 @@ #ifdef VEC_COMPILER_HAS_ALTIVEC vector unsigned short altivec; #endif - uint16_t generic[8]; + vuint16x4 generic[2]; } vuint16x8; typedef union { @@ -452,7 +664,7 @@ #ifdef VEC_COMPILER_HAS_ALTIVEC vector unsigned int altivec; #endif - uint32_t generic[4]; + vuint32x2 generic[2]; } vuint32x4; typedef union { @@ -472,7 +684,7 @@ #ifdef VEC_COMPILER_HAS_ALTIVEC vector signed char altivec; #endif - int8_t generic[16]; + vint8x8 generic[2]; } vint8x16; typedef union { @@ -482,7 +694,7 @@ #ifdef VEC_COMPILER_HAS_ALTIVEC vector signed short altivec; #endif - int16_t generic[8]; + vint16x4 generic[2]; } vint16x8; typedef union { @@ -492,7 +704,7 @@ #ifdef VEC_COMPILER_HAS_ALTIVEC vector signed int altivec; #endif - int32_t generic[4]; + vint32x2 generic[2]; } vint32x4; typedef union { @@ -507,1162 +719,615 @@ // 256-bit typedef union { +#ifdef VEC_COMPILER_HAS_AVX2 + __m256i avx2; +#endif vuint8x16 generic[2]; } vuint8x32; typedef union { +#ifdef VEC_COMPILER_HAS_AVX2 + __m256i avx2; +#endif vuint16x8 generic[2]; } vuint16x16; typedef union { +#ifdef VEC_COMPILER_HAS_AVX2 + __m256i avx2; +#endif vuint32x4 generic[2]; } vuint32x8; typedef union { +#ifdef VEC_COMPILER_HAS_AVX2 + __m256i avx2; +#endif vuint64x2 generic[2]; } vuint64x4; typedef union { +#ifdef VEC_COMPILER_HAS_AVX2 + __m256i avx2; +#endif vint8x16 generic[2]; } vint8x32; typedef union { +#ifdef VEC_COMPILER_HAS_AVX2 + __m256i avx2; +#endif vint16x8 generic[2]; } vint16x16; typedef union { +#ifdef VEC_COMPILER_HAS_AVX2 + __m256i avx2; +#endif vint32x4 generic[2]; } vint32x8; typedef union { +#ifdef VEC_COMPILER_HAS_AVX2 + __m256i avx2; +#endif vint64x2 generic[2]; } vint64x4; // 512-bit typedef union { +#ifdef VEC_COMPILER_HAS_AVX512F + __m512i avx512f; +#endif vuint8x32 generic[2]; } vuint8x64; typedef union { +#ifdef VEC_COMPILER_HAS_AVX512F + __m512i avx512f; +#endif vuint16x16 generic[2]; } vuint16x32; typedef union { +#ifdef VEC_COMPILER_HAS_AVX512F + __m512i avx512f; +#endif vuint32x8 generic[2]; } vuint32x16; typedef union { +#ifdef VEC_COMPILER_HAS_AVX512F + __m512i avx512f; +#endif vuint64x4 generic[2]; } vuint64x8; typedef union { +#ifdef VEC_COMPILER_HAS_AVX512F + __m512i avx512f; +#endif vint8x32 generic[2]; } vint8x64; typedef union { +#ifdef VEC_COMPILER_HAS_AVX512F + __m512i avx512f; +#endif vint16x16 generic[2]; } vint16x32; typedef union { +#ifdef VEC_COMPILER_HAS_AVX512F + __m512i avx512f; +#endif vint32x8 generic[2]; } vint32x16; typedef union { +#ifdef VEC_COMPILER_HAS_AVX512F + __m512i avx512f; +#endif vint64x4 generic[2]; } vint64x8; -// -------------------------------------------------------------------------------- -// okay, now onto the actual functions: -// -// we have generic variations of every major operation EXCEPT aligned load and -// aligned store. this means that a vector implementation can be created with -// only aligned load and aligned store implemented, which sucks, but it werks +// --------------------------------------------------------------------------------- +// function declarations + +int vec_init(void); + +#define VEC_DECLARE_OPERATIONS_SIGN(sign, bits, size) \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_splat(sign##int##bits##_t x); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_load_aligned(const sign##int##bits##_t in[size]); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_load(const sign##int##bits##_t in[size]); \ + void v##sign##int##bits##x##size##_store_aligned(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]); \ + void v##sign##int##bits##x##size##_store(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); -#define VEC_GENERIC_OPERATION(op, sign, csign, bits, size) \ - do { \ - V##csign##INT##bits##x##size##_ALIGNED_ARRAY(varr1); \ - V##csign##INT##bits##x##size##_ALIGNED_ARRAY(varr2); \ - \ - v##sign##int##bits##x##size##_store_aligned(vec1, varr1); \ - v##sign##int##bits##x##size##_store_aligned(vec2, varr2); \ - \ - for (int i = 0; i < size; i++) varr1[i] = (op); \ - \ - return v##sign##int##bits##x##size##_load_aligned(varr1); \ - } while (0) +#define VEC_DECLARE_OPERATIONS(bits, size) \ + VEC_DECLARE_OPERATIONS_SIGN( , bits, size) \ + VEC_DECLARE_OPERATIONS_SIGN(u, bits, size) + +// 64-bit +VEC_DECLARE_OPERATIONS(8, 8) +VEC_DECLARE_OPERATIONS(16, 4) +VEC_DECLARE_OPERATIONS(32, 2) + +// 128-bit +VEC_DECLARE_OPERATIONS(8, 16) +VEC_DECLARE_OPERATIONS(16, 8) +VEC_DECLARE_OPERATIONS(32, 4) +VEC_DECLARE_OPERATIONS(64, 2) -#define VEC_GENERIC_ADD(sign, csign, bits, size) VEC_GENERIC_OPERATION(varr1[i] + varr2[i], sign, csign, bits, size) -#define VEC_GENERIC_SUB(sign, csign, bits, size) VEC_GENERIC_OPERATION(varr1[i] - varr2[i], sign, csign, bits, size) -#define VEC_GENERIC_MUL(sign, csign, bits, size) VEC_GENERIC_OPERATION(varr1[i] * varr2[i], sign, csign, bits, size) -#define VEC_GENERIC_DIV(sign, csign, bits, size) VEC_GENERIC_OPERATION(varr2[i] ? (varr1[i] / varr2[i]) : 0, sign, csign, bits, size) -#define VEC_GENERIC_AND(sign, csign, bits, size) VEC_GENERIC_OPERATION(varr1[i] & varr2[i], sign, csign, bits, size) -#define VEC_GENERIC_OR(sign, csign, bits, size) VEC_GENERIC_OPERATION(varr1[i] | varr2[i], sign, csign, bits, size) -#define VEC_GENERIC_XOR(sign, csign, bits, size) VEC_GENERIC_OPERATION(varr1[i] ^ varr2[i], sign, csign, bits, size) +// 256-bit +VEC_DECLARE_OPERATIONS(8, 32) +VEC_DECLARE_OPERATIONS(16, 16) +VEC_DECLARE_OPERATIONS(32, 8) +VEC_DECLARE_OPERATIONS(64, 4) -#define VEC_GENERIC_CMP(op, sign, csign, bits, size) \ - VEC_GENERIC_OPERATION((varr1[i] op varr1[i]) ? csign##INT##bits##_MAX : 0, sign, csign, bits, size) +// 512-bit +VEC_DECLARE_OPERATIONS(8, 64) +VEC_DECLARE_OPERATIONS(16, 32) +VEC_DECLARE_OPERATIONS(32, 16) +VEC_DECLARE_OPERATIONS(64, 8) -#define VEC_GENERIC_CMPLT(sign, csign, bits, size) VEC_GENERIC_CMP(<, sign, csign, bits, size) -#define VEC_GENERIC_CMPLE(sign, csign, bits, size) VEC_GENERIC_CMP(<=, sign, csign, bits, size) -#define VEC_GENERIC_CMPEQ(sign, csign, bits, size) VEC_GENERIC_CMP(==, sign, csign, bits, size) -#define VEC_GENERIC_CMPGE(sign, csign, bits, size) VEC_GENERIC_CMP(>=, sign, csign, bits, size) -#define VEC_GENERIC_CMPGT(sign, csign, bits, size) VEC_GENERIC_CMP(>, sign, csign, bits, size) +#undef VEC_DECLARE_OPERATIONS +#undef VEC_DECLARE_OPERATIONS_SIGN + +// --------------------------------------------------------------------------------- +// okay, now we can actually implement the functions + +#ifdef VEC_IMPLEMENTATION -#define VEC_GENERIC_SHIFT(op, sign, csign, bits, size) \ - do { \ - V##csign##INT##bits##x##size##_ALIGNED_ARRAY(varr1); \ - VUINT##bits##x##size##_ALIGNED_ARRAY(varr2); \ - \ - v##sign##int##bits##x##size##_store_aligned(vec1, varr1); \ - vuint##bits##x##size##_store_aligned(vec2, varr2); \ - \ - for (int i = 0; i < size; i++) varr1[i] = (op); \ - \ - return v##sign##int##bits##x##size##_load_aligned(varr1); \ - } while (0) +// Fallback functions, need to be defined before everything else. +#include "impl/fallback.h" -#define VEC_GENERIC_LSHIFT(sign, csign, bits, size) VEC_GENERIC_SHIFT(vec_##sign##lshift(varr1[i], varr2[i]), sign, csign, bits, size) -#define VEC_GENERIC_RSHIFT(sign, csign, bits, size) VEC_GENERIC_SHIFT(vec_##sign##rshift(varr1[i], varr2[i]), sign, csign, bits, size) -#define VEC_GENERIC_LRSHIFT(sign, csign, bits, size) VEC_GENERIC_SHIFT(vec_##sign##lrshift(varr1[i], varr2[i]), sign, csign, bits, size) +// okay, these are filled in for each supported backend +#define VEC_DEFINE_IMPL_STRUCT_SIGN(sign, bits, size) \ + typedef struct { \ + v##sign##int##bits##x##size (*splat)(sign##int##bits##_t x); \ + v##sign##int##bits##x##size (*load_aligned)(const sign##int##bits##_t in[size]); \ + v##sign##int##bits##x##size (*load)(const sign##int##bits##_t in[size]); \ + void (*store_aligned)(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]); \ + void (*store)(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]); \ + v##sign##int##bits##x##size (*add)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*sub)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*mul)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*div)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*avg)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*and)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*or)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*xor)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*not)(v##sign##int##bits##x##size vec); \ + v##sign##int##bits##x##size (*cmplt)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*cmple)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*cmpeq)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*cmpge)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*cmpgt)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*lshift)(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ + v##sign##int##bits##x##size (*rshift)(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ + v##sign##int##bits##x##size (*lrshift)(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ + } v##sign##int##bits##x##size##_impl; -#ifdef VEC_COMPILER_HAS_SSE2 -// these are shared between SSE2 variations -# define VEC_SSE2_MUL_8x16(sign) \ - do { \ - /* unpack and multiply */ \ - __m128i dst_even = _mm_mullo_epi16(vec1.sse, vec2.sse); \ - __m128i dst_odd = _mm_mullo_epi16(_mm_srli_epi16(vec1.sse, 8), _mm_srli_epi16(vec2.sse, 8)); \ - \ - /* repack */ \ - return (v##sign##int8x16){ .sse = _mm_or_si128( \ - _mm_slli_epi16(dst_odd, 8), \ - _mm_srli_epi16(_mm_slli_epi16(dst_even, 8), 8) \ - )}; \ - } while (0) +#define VEC_DEFINE_IMPL_STRUCT(bits, size) \ + VEC_DEFINE_IMPL_STRUCT_SIGN( , bits, size) \ + VEC_DEFINE_IMPL_STRUCT_SIGN(u, bits, size) -# define VEC_SSE2_MUL_16x8(sign) \ - do { \ - /* we have a real instruction for this */ \ - return (v##sign##int16x8){ .sse = _mm_mullo_epi16(vec1.sse, vec2.sse) }; \ - } while (0) +// 64-bit +VEC_DEFINE_IMPL_STRUCT(8, 8) +VEC_DEFINE_IMPL_STRUCT(16, 4) +VEC_DEFINE_IMPL_STRUCT(32, 2) + +// 128-bit +VEC_DEFINE_IMPL_STRUCT(8, 16) +VEC_DEFINE_IMPL_STRUCT(16, 8) +VEC_DEFINE_IMPL_STRUCT(32, 4) +VEC_DEFINE_IMPL_STRUCT(64, 2) -# define VEC_SSE2_MUL_32x4(sign) \ - do { \ - /* this was stolen from... somewhere :) */ \ - __m128i a13 = _mm_shuffle_epi32(vec1.sse, 0xF5); /* (-,a3,-,a1) */ \ - __m128i b13 = _mm_shuffle_epi32(vec2.sse, 0xF5); /* (-,b3,-,b1) */ \ - __m128i prod02 = _mm_mul_epu32(vec1, vec2); /* (-,a2*b2,-,a0*b0) */ \ - __m128i prod13 = _mm_mul_epu32(a13, b13); /* (-,a3*b3,-,a1*b1) */ \ - __m128i prod01 = _mm_unpacklo_epi32(prod02,prod13); /* (-,-,a1*b1,a0*b0) */ \ - __m128i prod23 = _mm_unpackhi_epi32(prod02,prod13); /* (-,-,a3*b3,a2*b2) */ \ - return (v##sign##int32x4) {.sse = _mm_unpacklo_epi64(prod01, prod23)}; /* (ab3,ab2,ab1,ab0) */ \ - } while (0) +// 256-bit +VEC_DEFINE_IMPL_STRUCT(8, 32) +VEC_DEFINE_IMPL_STRUCT(16, 16) +VEC_DEFINE_IMPL_STRUCT(32, 8) +VEC_DEFINE_IMPL_STRUCT(64, 4) -# define VEC_SSE2_MUL_64x2(sign) \ - do { \ - __m128i ac = _mm_mul_epu32(vec1.sse, vec2.sse); /* ac = (vec1 & UINT32_MAX) * (vec2 & UINT32_MAX); */ \ - __m128i b = _mm_srli_epi64(vec1.sse, 32); /* b = vec1 >> 32; */ \ - __m128i bc = _mm_mul_epu32(b, vec2.sse); /* bc = b * (vec2 & UINT32_MAX); */ \ - __m128i d = _mm_srli_epi64(vec2.sse, 32); /* d = vec2 >> 32; */ \ - __m128i ad = _mm_mul_epu32(vec1.sse, d); /* ad = (vec1 & UINT32_MAX) * d; */ \ - __m128i hi = _mm_add_epi64(bc, ad); /* hi = bc + ad; */ \ - hi = _mm_slli_epi64(hi, 32); /* hi <<= 32; */ \ - return (v##sign##int64x2) {.sse = _mm_add_epi64(hi, ac); } /* return ac + hi; */ \ - } while (0) +// 512-bit +VEC_DEFINE_IMPL_STRUCT(8, 64) +VEC_DEFINE_IMPL_STRUCT(16, 32) +VEC_DEFINE_IMPL_STRUCT(32, 16) +VEC_DEFINE_IMPL_STRUCT(64, 8) + +#undef VEC_DEFINE_IMPL_STRUCT +#undef VEC_DEFINE_IMPL_STRUCT_SIGN + +// ------------------------------------------------------------------------ + +#ifdef VEC_COMPILER_HAS_ALTIVEC +# include "impl/ppc/altivec.h" #endif -// -------------------------------------------------------------------------------- -// vuint8x16 implementation - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_load_aligned(const uint8_t in[16]) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vuint8x16) { .sse = _mm_load_si128((__m128i *)in) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_ld(0, in); - } else -#endif - { - vuint8x16 vec; - memcpy(vec.generic, in, sizeof(vec.generic)); - return vec; - } - - VEC_ASSERT(0, "No suitable load_aligned variant found"); - - return (vuint8x16){ 0 }; -} - -VEC_FUNC_KEYWORDS void vuint8x16_store_aligned(vuint8x16 vec, uint8_t out[16]) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - _mm_store_si128((__m128i *)out, vec.sse); - return; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - vec_st(vec.altivec, 0, out); - return; - } else -#endif - { - memcpy(out, vec.generic, sizeof(vec.generic)); - return; - } - - VEC_ASSERT(0, "No suitable aligned store variant found"); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_splat(uint8_t x) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else +#ifdef VEC_COMPILER_HAS_AVX512F +# include "impl/x86/avx512f.h" #endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return (vuint8x16){ .altivec = vec_splat_u8(x) }; - } else -#endif - { - return (vuint8x16){ .generic = {x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x} }; - } - - // okay, we don't have a regular thing. call the load function with a splatted array - VUINT8x16_ALIGNED_ARRAY(arr); - for (int i = 0; i < 16; i++) arr[i] = x; - return vuint8x16_load_aligned(arr); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_load(const uint8_t in[16]) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vuint8x16) { .sse = _mm_loadu_si128((__m128i *)in) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_perm(vec_ld(0, in), vec_ld(16, in), vec_lvsl(0, in)); - } else -#endif - { - vuint8x16 vec; - memcpy(vec.generic, in, sizeof(vec.generic)); - return vec; - } - - // ok, we don't have unaligned load, copy the array - // and call the aligned load function - VUINT8x16_ALIGNED_ARRAY(aligned_in); - memcpy(aligned_in, in, 16); - return vuint8x16_load_aligned(aligned_in); -} - -VEC_FUNC_KEYWORDS void vuint8x16_store(vuint8x16 vec, uint8_t out[16]) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - _mm_storeu_si128((__m128i *)out, vec.sse); - return; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // noop - } else -#endif - { - memcpy(out, vec.generic, sizeof(vec.generic)); - return; - } - // no unaligned store? use the aligned version - VUINT8x16_ALIGNED_ARRAY(aligned_out); - vuint8x16_store_aligned(vec, aligned_out); - - // then copy to the output buffer - memcpy(out, aligned_out, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_add(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vuint8x16) { .sse = _mm_add_epi8(vec1.sse, vec2.sse) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_add(vec1.altivec, vec2.altivec); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] += vec2.generic[i]; - return vec1; - } - - VEC_GENERIC_ADD(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_sub(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vuint8x16) { .sse = _mm_sub_epi8(vec1.sse, vec2.sse) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_sub(vec1.altivec, vec2.altivec); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] -= vec2.generic[i]; - return vec1; - } - - VEC_GENERIC_SUB(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_mul(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - VEC_SSE2_MUL_8x16(u); - } else +#ifdef VEC_COMPILER_HAS_AVX2 +# include "impl/x86/avx2.h" #endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { -# ifdef vec_mul // this isn't available on older compilers - return vec_mul(vec1.altivec, vec2.altivec); -# endif - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] *= vec2.generic[i]; - return vec1; - } - VEC_GENERIC_MUL(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_div(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC_VSX - if (VEC_CPU_have_ALTIVEC_VSX()) { - return vec_div(vec1.altivec, vec2.altivec); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = vec2.generic[i] ? (vec1.generic[i] / vec2.generic[i]) : 0; - return vec1; - } - - VEC_GENERIC_DIV(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_and(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vuint8x16) { .sse = _mm_and_si128(vec1.sse, vec2.sse) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_and(vec1.altivec, vec2.altivec); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] &= vec2.generic[i]; - return vec1; - } - - VEC_GENERIC_AND(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_or(vuint8x16 vec1, vuint8x16 vec2) -{ #ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vuint8x16) { .sse = _mm_or_si128(vec1.sse, vec2.sse) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_or(vec1.altivec, vec2.altivec); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] |= vec2.generic[i]; - return vec1; - } - - VEC_GENERIC_OR(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_xor(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vuint8x16) { .sse = _mm_xor_si128(vec1.sse, vec2.sse) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_xor(vec1.altivec, vec2.altivec); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] ^= vec2.generic[i]; - return vec1; - } - - VEC_GENERIC_XOR(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_lshift(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - //noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_sl(vec1, vec2); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = vec_ulshift(vec1.generic[i], vec2.generic[i]); - return vec1; - } - - VEC_GENERIC_LSHIFT(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_rshift(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - //noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_sl(vec1, vec2); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = vec_urshift(vec1.generic[i], vec2.generic[i]); - return vec1; - } - - VEC_GENERIC_RSHIFT(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_lrshift(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - //noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_sl(vec1, vec2); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = vec_ulrshift(vec1.generic[i], vec2.generic[i]); - return vec1; - } - - VEC_GENERIC_LRSHIFT(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_avg(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_avg(vec1.altivec, vec2.altivec); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (uint8_t)(vec1.generic[i] + vec2.generic[i]) / 2; - return vec1; - } - - return vuint8x16_div(vuint8x16_add(vec1, vec2), vuint8x16_splat(2)); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_cmplt(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // these functions exist, no internet rn tho - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (vec1.generic[i] < vec2.generic[i]) ? UINT8_MAX : 0; - return vec1; - } - - VEC_GENERIC_CMPLT(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_cmple(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // these functions exist, no internet rn tho - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (vec1.generic[i] <= vec2.generic[i]) ? UINT8_MAX : 0; - return vec1; - } - - VEC_GENERIC_CMPLE(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_cmpeq(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // these functions exist, no internet rn tho - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (vec1.generic[i] == vec2.generic[i]) ? UINT8_MAX : 0; - return vec1; - } - - VEC_GENERIC_CMPEQ(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_cmpgt(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // these functions exist, no internet rn tho - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (vec1.generic[i] > vec2.generic[i]) ? UINT8_MAX : 0; - return vec1; - } - - VEC_GENERIC_CMPGT(u, U, 8, 16); -} - -VEC_FUNC_KEYWORDS vuint8x16 vuint8x16_cmpge(vuint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // these functions exist, no internet rn tho - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (vec1.generic[i] >= vec2.generic[i]) ? UINT8_MAX : 0; - return vec1; - } - - VEC_GENERIC_CMPGE(u, U, 8, 16); -} - -// -------------------------------------------------------------------------------- -// vint8x16 implementation - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_load_aligned(const int8_t in[16]) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vint8x16) { .sse = _mm_load_si128((__m128i *)in) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return (vint8x16) { .altivec = vec_ld(0, in) }; - } else +# include "impl/x86/sse2.h" #endif - { - vint8x16 vec; - memcpy(vec.generic, in, sizeof(vec.generic)); - return vec; - } - - VEC_ASSERT(0, "No suitable load_aligned variant found"); - - return (vint8x16){ 0 }; -} - -VEC_FUNC_KEYWORDS void vint8x16_store_aligned(vint8x16 vec, int8_t out[16]) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - _mm_store_si128((__m128i *)out, vec.sse); - return; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - vec_st(vec.altivec, 0, out); - return; - } else -#endif - { - memcpy(out, vec.generic, sizeof(vec.generic)); - return; - } - - VEC_ASSERT(0, "No suitable aligned store variant found"); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_splat(int8_t x) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return (vint8x16){ .altivec = vec_splat_s8(x) }; - } else -#endif - { - return (vint8x16){ .generic = {x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x} }; - } - - // okay, we don't have a regular thing. call the load function with a splatted array - VINT8x16_ALIGNED_ARRAY(arr); - for (int i = 0; i < 16; i++) arr[i] = x; - return vint8x16_load_aligned(arr); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_load(const int8_t in[16]) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vint8x16) { .sse = _mm_loadu_si128((__m128i *)in) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return (vint8x16) { .altivec = vec_perm(vec_ld(0, in), vec_ld(16, in), vec_lvsl(0, in)) }; - } else -#endif - { - vint8x16 vec; - memcpy(vec.generic, in, sizeof(vec.generic)); - return vec; - } - - // ok, we don't have unaligned load, copy the array - // and call the aligned load function - VINT8x16_ALIGNED_ARRAY(aligned_in); - memcpy(aligned_in, in, 16); - return vint8x16_load_aligned(aligned_in); -} - -VEC_FUNC_KEYWORDS void vint8x16_store(vint8x16 vec, int8_t out[16]) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - _mm_storeu_si128((__m128i *)out, vec.sse); - return; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // noop - } else -#endif - { - memcpy(out, vec.generic, sizeof(vec.generic)); - return; - } - - // no unaligned store? use the aligned version - VINT8x16_ALIGNED_ARRAY(aligned_out); - vint8x16_store_aligned(vec, aligned_out); - - // then copy to the output buffer - memcpy(out, aligned_out, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_add(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vint8x16) { .sse = _mm_add_epi8(vec1.sse, vec2.sse) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return (vint8x16) { .altivec = vec_add(vec1.altivec, vec2.altivec) }; - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] += vec2.generic[i]; - return vec1; - } - - VEC_GENERIC_ADD(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_sub(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vint8x16) { .sse = _mm_sub_epi8(vec1.sse, vec2.sse) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return (vint8x16) { .altivec = vec_sub(vec1.altivec, vec2.altivec) }; - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] -= vec2.generic[i]; - return vec1; - } - - VEC_GENERIC_SUB(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_mul(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - VEC_SSE2_MUL_8x16(); - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { -# ifdef vec_mul // this isn't available on older compilers - return (vint8x16) { .altivec = vec_mul(vec1.altivec, vec2.altivec) }; -# endif - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] *= vec2.generic[i]; - return vec1; - } - - VEC_GENERIC_MUL(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_div(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC_VSX - if (VEC_CPU_have_ALTIVEC_VSX()) { - return (vint8x16) { .altivec = vec_div(vec1.altivec, vec2.altivec) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // noop - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = vec2.generic[i] ? (vec1.generic[i] / vec2.generic[i]) : 0; - return vec1; - } - - VEC_GENERIC_DIV(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_and(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vint8x16) { .sse = _mm_and_si128(vec1.sse, vec2.sse) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return (vint8x16) {.altivec = vec_and(vec1.altivec, vec2.altivec) }; - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] &= vec2.generic[i]; - return vec1; - } - - VEC_GENERIC_ADD(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_or(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vint8x16) { .sse = _mm_or_si128(vec1.sse, vec2.sse) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_or(vec1.altivec, vec2.altivec); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] |= vec2.generic[i]; - return vec1; - } - VEC_GENERIC_OR(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_xor(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - return (vint8x16) { .sse = _mm_xor_si128(vec1.sse, vec2.sse) }; - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_xor(vec1.altivec, vec2.altivec); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] ^= vec2.generic[i]; - return vec1; - } - - VEC_GENERIC_XOR(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_lshift(vint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - //noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_sl(vec1, vec2); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = vec_lshift(vec1.generic[i], vec2.generic[i]); - return vec1; - } - - VEC_GENERIC_LSHIFT(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_rshift(vint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - //noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_sl(vec1, vec2); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = vec_rshift(vec1.generic[i], vec2.generic[i]); - return vec1; - } - - VEC_GENERIC_RSHIFT(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_lrshift(vint8x16 vec1, vuint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - //noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_sl(vec1, vec2); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = vec_lrshift(vec1.generic[i], vec2.generic[i]); - return vec1; - } - - VEC_GENERIC_LRSHIFT(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_avg(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - return vec_avg(vec1.altivec, vec2.altivec); - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (int8_t)(vec1.generic[i] + vec2.generic[i]) / 2; - return vec1; - } - - return vint8x16_div(vint8x16_add(vec1, vec2), vint8x16_splat(2)); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_cmplt(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // these functions exist, no internet rn tho - } else +// depends on SSE2 functions; the only thing SSE4.1 provides for us +// is a native 32-bit multiply +#ifdef VEC_COMPILER_HAS_SSE41 +# include "impl/x86/sse41.h" #endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (vec1.generic[i] < vec2.generic[i]) ? UINT8_MAX : 0; - return vec1; - } - VEC_GENERIC_CMPLT(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_cmple(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // these functions exist, no internet rn tho - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (vec1.generic[i] <= vec2.generic[i]) ? UINT8_MAX : 0; - return vec1; - } - - VEC_GENERIC_CMPLE(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_cmpeq(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // these functions exist, no internet rn tho :) - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (vec1.generic[i] == vec2.generic[i]) ? UINT8_MAX : 0; - return vec1; - } - - VEC_GENERIC_CMPEQ(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_cmpgt(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // these functions exist, no internet rn tho - } else +#ifdef VEC_COMPILER_HAS_MMX +# include "impl/x86/mmx.h" #endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (vec1.generic[i] > vec2.generic[i]) ? UINT8_MAX : 0; - return vec1; - } - VEC_GENERIC_CMPGT(, , 8, 16); -} - -VEC_FUNC_KEYWORDS vint8x16 vint8x16_cmpge(vint8x16 vec1, vint8x16 vec2) -{ -#ifdef VEC_COMPILER_HAS_SSE2 - if (VEC_CPU_have_SSE2()) { - // noop - } else -#endif -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (VEC_CPU_have_ALTIVEC()) { - // these functions exist, no internet rn tho - } else -#endif - { - for (int i = 0; i < 16; i++) vec1.generic[i] = (vec1.generic[i] >= vec2.generic[i]) ? UINT8_MAX : 0; - return vec1; - } - - VEC_GENERIC_CMPGE(, , 8, 16); -} - -/* ----------------------------------------------------------------- */ -/* bitwise NOT is just an XOR with UINT[BITS]_MAX */ - -#define DEFINE_NOT_OPERATION(sign, bits, size) \ - VEC_FUNC_KEYWORDS v##sign##int##bits##x##size v##sign##int##bits##x##size##_not(v##sign##int##bits##x##size vec) \ - { \ - return v##sign##int##bits##x##size##_xor(vec, v##sign##int##bits##x##size##_splat((sign##int##bits##_t)UINT##bits##_MAX)); \ - } - -DEFINE_NOT_OPERATION(, 8, 16) -DEFINE_NOT_OPERATION(u, 8, 16) - -DEFINE_NOT_OPERATION(, 8, 32) -DEFINE_NOT_OPERATION(, 16, 16) -DEFINE_NOT_OPERATION(, 32, 8) -DEFINE_NOT_OPERATION(, 64, 4) -DEFINE_NOT_OPERATION(u, 8, 32) -DEFINE_NOT_OPERATION(u, 16, 16) -DEFINE_NOT_OPERATION(u, 32, 8) -DEFINE_NOT_OPERATION(u, 64, 4) - -DEFINE_NOT_OPERATION(, 8, 64) -DEFINE_NOT_OPERATION(, 16, 32) -DEFINE_NOT_OPERATION(, 32, 16) -DEFINE_NOT_OPERATION(, 64, 8) -DEFINE_NOT_OPERATION(u, 8, 64) -DEFINE_NOT_OPERATION(u, 16, 32) -DEFINE_NOT_OPERATION(u, 32, 16) -DEFINE_NOT_OPERATION(u, 64, 8) - -#undef DEFINE_NOT_OPERATION +#include "impl/generic.h" /* ---------------------------------------------------------------- */ -/* cleanup */ -#undef VEC_OPERATION_DECL -#undef VEC_OPERATION_THIS_DECL -#undef VEC_TWOWAY_DECL +#include "impl/cpu.h" // CPU detection crap + +// 64-bit +static vint8x8_impl *vint8x8_impl_cpu = &vint8x8_impl_generic; +static vuint8x8_impl *vuint8x8_impl_cpu = &vuint8x8_impl_generic; +static vint16x4_impl *vint16x4_impl_cpu = &vint16x4_impl_generic; +static vuint16x4_impl *vuint16x4_impl_cpu = &vuint16x4_impl_generic; +static vint32x2_impl *vint32x2_impl_cpu = &vint32x2_impl_generic; +static vuint32x2_impl *vuint32x2_impl_cpu = &vuint32x2_impl_generic; + +// 128-bit +static vint8x16_impl *vint8x16_impl_cpu = &vint8x16_impl_generic; +static vuint8x16_impl *vuint8x16_impl_cpu = &vuint8x16_impl_generic; +static vint16x8_impl *vint16x8_impl_cpu = &vint16x8_impl_generic; +static vuint16x8_impl *vuint16x8_impl_cpu = &vuint16x8_impl_generic; +static vint32x4_impl *vint32x4_impl_cpu = &vint32x4_impl_generic; +static vuint32x4_impl *vuint32x4_impl_cpu = &vuint32x4_impl_generic; +static vint64x2_impl *vint64x2_impl_cpu = &vint64x2_impl_generic; +static vuint64x2_impl *vuint64x2_impl_cpu = &vuint64x2_impl_generic; + +// 256-bit +static vint8x32_impl *vint8x32_impl_cpu = &vint8x32_impl_generic; +static vuint8x32_impl *vuint8x32_impl_cpu = &vuint8x32_impl_generic; +static vint16x16_impl *vint16x16_impl_cpu = &vint16x16_impl_generic; +static vuint16x16_impl *vuint16x16_impl_cpu = &vuint16x16_impl_generic; +static vint32x8_impl *vint32x8_impl_cpu = &vint32x8_impl_generic; +static vuint32x8_impl *vuint32x8_impl_cpu = &vuint32x8_impl_generic; +static vint64x4_impl *vint64x4_impl_cpu = &vint64x4_impl_generic; +static vuint64x4_impl *vuint64x4_impl_cpu = &vuint64x4_impl_generic; + +// 512-bit +static vint8x64_impl *vint8x64_impl_cpu = &vint8x64_impl_generic; +static vuint8x64_impl *vuint8x64_impl_cpu = &vuint8x64_impl_generic; +static vint16x32_impl *vint16x32_impl_cpu = &vint16x32_impl_generic; +static vuint16x32_impl *vuint16x32_impl_cpu = &vuint16x32_impl_generic; +static vint32x16_impl *vint32x16_impl_cpu = &vint32x16_impl_generic; +static vuint32x16_impl *vuint32x16_impl_cpu = &vuint32x16_impl_generic; +static vint64x8_impl *vint64x8_impl_cpu = &vint64x8_impl_generic; +static vuint64x8_impl *vuint64x8_impl_cpu = &vuint64x8_impl_generic; + +int vec_init(void) +{ + // This function is NOT thread safe. However, once vec + // is initialized, all of the vector functions are thread-safe. + // + // In fact, it's possible to use vec without calling + // vec_init() at all, but it would be completely useless since + // it would just use a generic implementation without any + // vectorization whatsoever (unless maybe the compiler is + // smart enough to optimize it into vectors) + + vec_get_CPU_features(); -#undef VEC_DECL_SPLAT -#undef VEC_DECL_LOAD -#undef VEC_DECL_STORE -#undef VEC_DECL_ADD -#undef VEC_DECL_SUB -#undef VEC_DECL_MUL -#undef VEC_DECL_DIV -#undef VEC_DECL_AND -#undef VEC_DECL_OR -#undef VEC_DECL_XOR -#undef VEC_DECL_AVG -#undef VEC_DECL_SHIFT -#undef VEC_DECL_NOT +#ifdef VEC_COMPILER_HAS_ALTIVEC + if (vec_CPU_have_ALTIVEC()) { + vint8x16_impl_cpu = &vint8x16_impl_altivec; + vuint8x16_impl_cpu = &vuint8x16_impl_altivec; + vint16x8_impl_cpu = &vint16x8_impl_altivec; + vuint16x8_impl_cpu = &vuint16x8_impl_altivec; + vint32x4_impl_cpu = &vint32x4_impl_altivec; + vuint32x4_impl_cpu = &vuint32x4_impl_altivec; +#ifdef VEC_COMPILER_HAS_ALTIVEC_VSX + if (vec_CPU_have_ALTIVEC_VSX()) { + vint64x2_impl_cpu = &vint64x2_impl_altivec; + vuint64x2_impl_cpu = &vuint64x2_impl_altivec; + } +#endif + } +#endif +#ifdef VEC_COMPILER_HAS_AVX512F + if (vec_CPU_have_AVX512F()) { + vint8x64_impl_cpu = &vint8x64_impl_avx512f; + vuint8x64_impl_cpu = &vuint8x64_impl_avx512f; + vint16x32_impl_cpu = &vint16x32_impl_avx512f; + vuint16x32_impl_cpu = &vuint16x32_impl_avx512f; + vint32x16_impl_cpu = &vint32x16_impl_avx512f; + vuint32x16_impl_cpu = &vuint32x16_impl_avx512f; + vint64x8_impl_cpu = &vint64x8_impl_avx512f; + vuint64x8_impl_cpu = &vuint64x8_impl_avx512f; + } +#endif +#ifdef VEC_COMPILER_HAS_AVX2 + if (vec_CPU_have_AVX2()) { + vint8x32_impl_cpu = &vint8x32_impl_avx2; + vuint8x32_impl_cpu = &vuint8x32_impl_avx2; + vint16x16_impl_cpu = &vint16x16_impl_avx2; + vuint16x16_impl_cpu = &vuint16x16_impl_avx2; + vint32x8_impl_cpu = &vint32x8_impl_avx2; + vuint32x8_impl_cpu = &vuint32x8_impl_avx2; + vint64x4_impl_cpu = &vint64x4_impl_avx2; + vuint64x4_impl_cpu = &vuint64x4_impl_avx2; + } +#endif +#ifdef VEC_COMPILER_HAS_SSE2 + if (vec_CPU_have_SSE2()) { + vint8x16_impl_cpu = &vint8x16_impl_sse2; + vuint8x16_impl_cpu = &vuint8x16_impl_sse2; + vint16x8_impl_cpu = &vint16x8_impl_sse2; + vuint16x8_impl_cpu = &vuint16x8_impl_sse2; +# ifdef VEC_COMPILER_HAS_SSE41 + if (vec_CPU_have_SSE41()) { + vint32x4_impl_cpu = &vint32x4_impl_sse41; + vuint32x4_impl_cpu = &vuint32x4_impl_sse41; + } else +# endif + { + vint32x4_impl_cpu = &vint32x4_impl_sse2; + vuint32x4_impl_cpu = &vuint32x4_impl_sse2; + } + vint64x2_impl_cpu = &vint64x2_impl_sse2; + vuint64x2_impl_cpu = &vuint64x2_impl_sse2; + } +#endif +#ifdef VEC_COMPILER_HAS_MMX + if (vec_CPU_have_MMX()) { + vint8x8_impl_cpu = &vint8x8_impl_mmx; + vuint8x8_impl_cpu = &vuint8x8_impl_mmx; + vint16x4_impl_cpu = &vint16x4_impl_mmx; + vuint16x4_impl_cpu = &vuint16x4_impl_mmx; + vint32x2_impl_cpu = &vint32x2_impl_mmx; + vuint32x2_impl_cpu = &vuint32x2_impl_mmx; + } +#endif + { + // do nothing, they're already set to generics + } +} -#undef VEC_DECL_CMPLT -#undef VEC_DECL_CMPGT -#undef VEC_DECL_CMPEQ -#undef VEC_DECL_CMPLE -#undef VEC_DECL_CMPGE +/* ---------------------------------------------------------------- */ -#undef VEC_GENERIC_SPLAT -#undef VEC_GENERIC_DIVIDE -#undef VEC_GENERIC_SHIFT -#undef VEC_GENERIC_SHIFTS -#undef VEC_GENERIC_LSHIFT -#undef VEC_GENERIC_RSHIFT -#undef VEC_GENERIC_LRSHIFT -#undef VEC_GENERIC_AVG -#undef VEC_GENERIC_THAN_OR_EQUAL -#undef VEC_GENERIC_COMPARISON -#undef VEC_GENERIC_COMPARISONS +#define VEC_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_splat(sign##int##bits##_t x) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->splat) \ + return v##sign##int##bits##x##size##_impl_cpu->splat(x); \ + \ + return v##sign##int##bits##x##size##_fallback_splat(x); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_load_aligned(const sign##int##bits##_t in[size]) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->load_aligned) \ + return v##sign##int##bits##x##size##_impl_cpu->load_aligned(in); \ + \ + VEC_ASSERT(0, "vec: load_aligned is required to be implemented"); \ + return (v##sign##int##bits##x##size){0}; \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_load(const sign##int##bits##_t in[size]) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->load) \ + return v##sign##int##bits##x##size##_impl_cpu->load(in); \ + \ + return v##sign##int##bits##x##size##_fallback_load(in); \ + } \ + \ + void v##sign##int##bits##x##size##_store_aligned(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->store_aligned) { \ + v##sign##int##bits##x##size##_impl_cpu->store_aligned(vec, out); \ + return; \ + } \ + \ + VEC_ASSERT(0, "vec: store_aligned is required to be implemented"); \ + } \ + \ + void v##sign##int##bits##x##size##_store(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->store) { \ + v##sign##int##bits##x##size##_impl_cpu->store(vec, out); \ + return; \ + } \ + \ + v##sign##int##bits##x##size##_fallback_store(vec, out); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->add) \ + v##sign##int##bits##x##size##_impl_cpu->add(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_add(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->sub) \ + v##sign##int##bits##x##size##_impl_cpu->sub(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_sub(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->mul) \ + v##sign##int##bits##x##size##_impl_cpu->mul(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_mul(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->div) \ + v##sign##int##bits##x##size##_impl_cpu->div(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_div(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->avg) \ + v##sign##int##bits##x##size##_impl_cpu->avg(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_avg(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->and) \ + v##sign##int##bits##x##size##_impl_cpu->and(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_and(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->or) \ + v##sign##int##bits##x##size##_impl_cpu->or(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_or(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->xor) \ + v##sign##int##bits##x##size##_impl_cpu->xor(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_xor(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_not(v##sign##int##bits##x##size vec) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->not) \ + v##sign##int##bits##x##size##_impl_cpu->not(vec); \ + \ + return v##sign##int##bits##x##size##_fallback_not(vec); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->cmplt) \ + v##sign##int##bits##x##size##_impl_cpu->cmplt(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_cmplt(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->cmple) \ + v##sign##int##bits##x##size##_impl_cpu->cmple(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_cmple(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->cmpeq) \ + v##sign##int##bits##x##size##_impl_cpu->cmpeq(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_cmpeq(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->cmpge) \ + v##sign##int##bits##x##size##_impl_cpu->cmpge(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_cmpge(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->cmpgt) \ + v##sign##int##bits##x##size##_impl_cpu->cmpgt(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_cmpgt(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->lshift) \ + v##sign##int##bits##x##size##_impl_cpu->lshift(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_lshift(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->rshift) \ + v##sign##int##bits##x##size##_impl_cpu->rshift(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_rshift(vec1, vec2); \ + } \ + \ + v##sign##int##bits##x##size v##sign##int##bits##x##size##_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + if (v##sign##int##bits##x##size##_impl_cpu->lrshift) \ + v##sign##int##bits##x##size##_impl_cpu->lrshift(vec1, vec2); \ + \ + return v##sign##int##bits##x##size##_fallback_lrshift(vec1, vec2); \ + } -#undef VEC_VINT8X16 -#undef VEC_VINT16X8 -#undef VEC_VINT32X4 -#undef VEC_VINT64X2 -#undef VEC_VUINT8X16 -#undef VEC_VUINT16X8 -#undef VEC_VUINT32X4 -#undef VEC_VUINT64X2 +#define VEC_DEFINE_OPERATIONS(bits, size) \ + VEC_DEFINE_OPERATIONS_SIGN( , bits, size) \ + VEC_DEFINE_OPERATIONS_SIGN(u, bits, size) + +// 64-bit +VEC_DEFINE_OPERATIONS(8, 8) +VEC_DEFINE_OPERATIONS(16, 4) +VEC_DEFINE_OPERATIONS(32, 2) + +// 128-bit +VEC_DEFINE_OPERATIONS(8, 16) +VEC_DEFINE_OPERATIONS(16, 8) +VEC_DEFINE_OPERATIONS(32, 4) +VEC_DEFINE_OPERATIONS(64, 2) -#undef VEC_VINT8X32 -#undef VEC_VINT16X16 -#undef VEC_VINT32X8 -#undef VEC_VINT64X4 -#undef VEC_VUINT8X32 -#undef VEC_VUINT16X16 -#undef VEC_VUINT32X8 -#undef VEC_VUINT64X4 +// 256-bit +VEC_DEFINE_OPERATIONS(8, 32) +VEC_DEFINE_OPERATIONS(16, 16) +VEC_DEFINE_OPERATIONS(32, 8) +VEC_DEFINE_OPERATIONS(64, 4) -#undef VEC_VINT8X64 -#undef VEC_VINT16X32 -#undef VEC_VINT32X16 -#undef VEC_VINT64X8 -#undef VEC_VUINT8X64 -#undef VEC_VUINT16X32 -#undef VEC_VUINT32X16 -#undef VEC_VUINT64X8 +// 512-bit +VEC_DEFINE_OPERATIONS(8, 64) +VEC_DEFINE_OPERATIONS(16, 32) +VEC_DEFINE_OPERATIONS(32, 16) +VEC_DEFINE_OPERATIONS(64, 8) + +#undef VEC_DEFINE_OPERATIONS +#undef VEC_DEFINE_OPERATIONS_SIGN + +#endif /* VEC_IMPLEMENTATION */ #endif /* VEC_VEC_H_ */
--- a/src/vec.c Tue Nov 19 15:55:01 2024 -0500 +++ b/src/vec.c Wed Nov 20 04:10:37 2024 -0500 @@ -1,3 +1,2 @@ -#define VEC_EXTERN -#define VEC_EXTERN_DEFINE +#define VEC_IMPLEMENTATION #include "vec/vec.h"
--- a/test/Makefile Tue Nov 19 15:55:01 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -CFLAGS += -std=c99 -I../include - -# binary files -BINS = test-gcc test-generic test-host -OBJS = $(BINS:=.o) - -.PHONY: all clean test - -all: $(BINS) - -# suppress the platform-dependent hardware stuff so we only have -# GCC vector extensions -test-gcc: CFLAGS += -DVEC_SUPPRESS_HW - -# also suppress GCC extensions, leaving only the defaults -test-generic: CFLAGS += -DVEC_SUPPRESS_HW -DVEC_SUPPRESS_GCC - -$(OBJS): main.c - $(CC) $(CFLAGS) -o $@ -c $^ - -$(BINS): %: %.o - $(CC) $(LDFLAGS) -o $@ $^ - -clean: - $(RM) $(BINS) $(OBJS) - -test: clean $(BINS) - ./test-gcc - ./test-generic - ./test-host
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/Makefile.ppc Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,3 @@ +CFLAGS += -maltivec + +include Makefile.template \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/Makefile.template Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,40 @@ +CFLAGS += -g -O2 -std=c99 -I../include + +HEADERS = ../include/vec/vec.h \ + ../include/vec/impl/ppc/altivec.h \ + ../include/vec/impl/x86/avx2.h \ + ../include/vec/impl/x86/avx512f.h \ + ../include/vec/impl/x86/mmx.h \ + ../include/vec/impl/x86/sse2.h \ + ../include/vec/impl/x86/sse41.h \ + ../include/vec/impl/cpu.h \ + ../include/vec/impl/fallback.h \ + ../include/vec/impl/generic.h +BINS = test-generic test-host +OBJS = vec-generic.o vec-host.o test.o + +.PHONY: all clean test + +all: $(BINS) + +vec-generic.o: ../src/vec.c + $(CC) $(CFLAGS) -DVEC_SUPPRESS_HW=1 -c -o $@ $< + +vec-host.o: ../src/vec.c + $(CC) $(CFLAGS) -c -o $@ $< + +test.o: test.c + $(CC) $(CFLAGS) -c -o $@ $< + +test-generic: vec-generic.o test.o + $(CC) $(LDFLAGS) -o $@ $^ + +test-host: vec-host.o test.o + $(CC) $(LDFLAGS) -o $@ $^ + +clean: + $(RM) $(BINS) $(OBJS) + +test: clean $(BINS) + ./test-generic + ./test-host
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/Makefile.x86 Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,3 @@ +CFLAGS += -mmmx -msse2 -msse4.1 -mavx2 -mavx512f + +include Makefile.template \ No newline at end of file
--- a/test/main.c Tue Nov 19 15:55:01 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -#define VEC_EXTERN -#define VEC_EXTERN_DEFINE -#include "vec/vec.h" - -#include <stdio.h> -#include <inttypes.h> - -#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) - -static const int8_t testval8[] = { - INT8_C(-80), INT8_C(-3), INT8_C(25), INT8_C(0x7F), - INT8_C(-42), INT8_C(27), INT8_C(24), INT8_C(0x40), -}; - -static const uint8_t testvalu8[] = { - UINT8_C(0x00), UINT8_C(0xFF), UINT8_C(0xFE), UINT8_C(0x7F), - UINT8_C(0xC0), UINT8_C(0x80), UINT8_C(0x20), UINT8_C(0x50), -}; - -static const int16_t testval16[] = { - INT16_C(-8000), INT16_C(-30), INT16_MAX, INT16_C(0x4000), - INT16_C(-42), INT16_C(250), INT16_MIN, INT16_C(0x500), -}; - -static const uint16_t testvalu16[] = { - UINT16_C(0x0000), UINT16_C(0xFFFF), UINT16_C(0xFEA), UINT16_C(0x7FF), - UINT16_C(0x7FFF), UINT16_C(0x8000), UINT16_C(0x20B), UINT16_C(0x50C), -}; - -static const int32_t testval32[] = { - INT32_C(-1000000), INT32_C(-3), INT32_C(0x00000000), INT32_C(0xFFFFFFFF), - INT32_C( -42), INT32_C(27), INT32_C(0xABCDEF03), INT32_C(0x00000FFF), - INT32_C(0xFFFFFFFF), INT32_C( 0), INT32_C(0xFFFFFFFE), INT32_C( 1), -}; - -static const uint32_t testvalu32[] = { - UINT32_C(0x00000000), UINT32_C(0xDEADBEEF), UINT32_C(42), UINT32_C(0x12340000), - UINT32_C(0xFFFFFFFF), UINT32_C(0xFEDCBA98), UINT32_C(17), UINT32_C(0x00012345), - UINT32_C(0xFFFFFFFF), UINT32_C(0xFFFFFFFE), UINT32_C( 0), UINT32_C( 1), -}; - -static const int64_t testval64[] = { - INT64_MAX, INT64_C(-3), INT64_C(0x00000000), INT64_C(0xFFFFFFFFF), - INT64_MIN, INT64_C(645366), INT64_C(0x12345ABCDE), INT64_C(0xF00000FFF), -}; - -static const uint64_t testvalu64[] = { - UINT64_MAX, UINT64_C(0x44354365), UINT64_C(0x00000000), UINT64_C(0xFFFFFFFFF), - UINT64_C(0xff), UINT64_C(645366), UINT64_C(0x12345ABCDE), UINT64_C(0xF00000FFF), -}; - -#define VTEST(sign, csign, bits, size) \ - static inline v##sign##int##bits##x##size vtest##sign##bits##x##size(const size_t start) \ - { \ - V##csign##INT##bits##x##size##_ALIGNED_ARRAY(x); \ - for (size_t i = 0; i < size; i++) \ - x[i] = testval##sign##bits[(start + i) % ARRAY_SIZE(testval##sign##bits)]; \ - return v##sign##int##bits##x##size##_load_aligned(x); \ - } - -#define VPRINT(sign, csign, psign, bits, size) \ - static inline void print_v##sign##int##bits##x##size(FILE *file, v##sign##int##bits##x##size vec) \ - { \ - fputs("vector: ", file); \ - \ - V##csign##INT##bits##x##size##_ALIGNED_ARRAY(v); \ - \ - v##sign##int##bits##x##size##_store_aligned(vec, v); \ - \ - fprintf(file, "%" PRI ## psign ## bits, v[0]); \ - \ - for (int i = 1; i < size; i++) \ - fprintf(file, ", %" PRI ## psign ## bits, v[i]); \ - \ - fputs("\n", file); \ - \ - } - -#define DEF_VEC_TEST_FUNCS(bits, size) \ - VTEST(, , bits, size) VTEST(u, U, bits, size) \ - VPRINT(, , d, bits, size) VPRINT(u, U, u, bits, size) - -DEF_VEC_TEST_FUNCS(8, 16) - -#undef DEF_VEC_TEST_FUNCS -#undef VPRINT -#undef VTEST - -// ------------------------------------------------------------ - -#include "test_align.h" -#include "test_arith.h" -#include "test_compare.h" - -// ------------------------------------------------------------ - -int main(void) -{ - int ret = 0; - - ret |= test_align(); - ret |= test_arith(); - ret |= test_compare(); - - return ret; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test.c Wed Nov 20 04:10:37 2024 -0500 @@ -0,0 +1,123 @@ +#include "vec/vec.h" + +#include <stdio.h> +#include <inttypes.h> + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + +static const int8_t testval8[] = { + INT8_C(-80), INT8_C(-3), INT8_C(25), INT8_C(0x7F), + INT8_C(-42), INT8_C(27), INT8_C(24), INT8_C(0x40), +}; + +static const uint8_t testvalu8[] = { + UINT8_C(0x00), UINT8_C(0xFF), UINT8_C(0xFE), UINT8_C(0x7F), + UINT8_C(0xC0), UINT8_C(0x80), UINT8_C(0x20), UINT8_C(0x50), +}; + +static const int16_t testval16[] = { + INT16_C(-8000), INT16_C(-30), INT16_MAX, INT16_C(0x4000), + INT16_C(-42), INT16_C(250), INT16_MIN, INT16_C(0x500), +}; + +static const uint16_t testvalu16[] = { + UINT16_C(0x0000), UINT16_C(0xFFFF), UINT16_C(0xFEA), UINT16_C(0x7FF), + UINT16_C(0x7FFF), UINT16_C(0x8000), UINT16_C(0x20B), UINT16_C(0x50C), +}; + +static const int32_t testval32[] = { + INT32_C(-1000000), INT32_C(-3), INT32_C(0x00000000), INT32_C(0xFFFFFFFF), + INT32_C( -42), INT32_C(27), INT32_C(0xABCDEF03), INT32_C(0x00000FFF), + INT32_C(0xFFFFFFFF), INT32_C( 0), INT32_C(0xFFFFFFFE), INT32_C( 1), +}; + +static const uint32_t testvalu32[] = { + UINT32_C(0x00000000), UINT32_C(0xDEADBEEF), UINT32_C(42), UINT32_C(0x12340000), + UINT32_C(0xFFFFFFFF), UINT32_C(0xFEDCBA98), UINT32_C(17), UINT32_C(0x00012345), + UINT32_C(0xFFFFFFFF), UINT32_C(0xFFFFFFFE), UINT32_C( 0), UINT32_C( 1), +}; + +static const int64_t testval64[] = { + INT64_MAX, INT64_C(-3), INT64_C(0x00000000), INT64_C(0xFFFFFFFFF), + INT64_MIN, INT64_C(645366), INT64_C(0x12345ABCDE), INT64_C(0xF00000FFF), +}; + +static const uint64_t testvalu64[] = { + UINT64_MAX, UINT64_C(0x44354365), UINT64_C(0x00000000), UINT64_C(0xFFFFFFFFF), + UINT64_C(0xff), UINT64_C(645366), UINT64_C(0x12345ABCDE), UINT64_C(0xF00000FFF), +}; + +#define VTEST(sign, csign, bits, size) \ + static inline v##sign##int##bits##x##size vtest##sign##bits##x##size(const size_t start) \ + { \ + V##csign##INT##bits##x##size##_ALIGNED_ARRAY(x); \ + for (size_t i = 0; i < size; i++) \ + x[i] = testval##sign##bits[(start + i) % ARRAY_SIZE(testval##sign##bits)]; \ + return v##sign##int##bits##x##size##_load_aligned(x); \ + } + +#define VPRINT(sign, csign, psign, bits, size) \ + static inline void print_v##sign##int##bits##x##size(FILE *file, v##sign##int##bits##x##size vec) \ + { \ + fputs("vector: ", file); \ + \ + V##csign##INT##bits##x##size##_ALIGNED_ARRAY(v); \ + \ + v##sign##int##bits##x##size##_store_aligned(vec, v); \ + \ + fprintf(file, "%" PRI ## psign ## bits, v[0]); \ + \ + for (int i = 1; i < size; i++) \ + fprintf(file, ", %" PRI ## psign ## bits, v[i]); \ + \ + fputs("\n", file); \ + \ + } + +#define DEF_VEC_TEST_FUNCS(bits, size) \ + VTEST(, , bits, size) VTEST(u, U, bits, size) \ + VPRINT(, , d, bits, size) VPRINT(u, U, u, bits, size) + +DEF_VEC_TEST_FUNCS(8, 8) +DEF_VEC_TEST_FUNCS(16, 4) +DEF_VEC_TEST_FUNCS(32, 2) + +DEF_VEC_TEST_FUNCS(8, 16) +DEF_VEC_TEST_FUNCS(16, 8) +DEF_VEC_TEST_FUNCS(32, 4) +DEF_VEC_TEST_FUNCS(64, 2) + +DEF_VEC_TEST_FUNCS(8, 32) +DEF_VEC_TEST_FUNCS(16, 16) +DEF_VEC_TEST_FUNCS(32, 8) +DEF_VEC_TEST_FUNCS(64, 4) + +DEF_VEC_TEST_FUNCS(8, 64) +DEF_VEC_TEST_FUNCS(16, 32) +DEF_VEC_TEST_FUNCS(32, 16) +DEF_VEC_TEST_FUNCS(64, 8) + +#undef DEF_VEC_TEST_FUNCS +#undef VPRINT +#undef VTEST + +// ------------------------------------------------------------ + +#include "test_align.h" +#include "test_arith.h" +#include "test_compare.h" + +// ------------------------------------------------------------ + +int main(void) +{ + int ret = 0; + + vec_init(); + + ret |= test_align(); + ret |= test_arith(); + ret |= test_compare(); + + return ret; +}
--- a/test/test_align.h Tue Nov 19 15:55:01 2024 -0500 +++ b/test/test_align.h Wed Nov 20 04:10:37 2024 -0500 @@ -31,7 +31,24 @@ RUN_TEST( , , bits, size) \ RUN_TEST(u, U, bits, size) + RUN_TESTS(8, 8) + RUN_TESTS(16, 4) + RUN_TESTS(32, 2) + RUN_TESTS(8, 16) + RUN_TESTS(16, 8) + RUN_TESTS(32, 4) + RUN_TESTS(64, 2) + + RUN_TESTS(8, 32) + RUN_TESTS(16, 16) + RUN_TESTS(32, 8) + RUN_TESTS(64, 4) + + RUN_TESTS(8, 64) + RUN_TESTS(16, 32) + RUN_TESTS(32, 16) + RUN_TESTS(64, 8) #undef RUN_TESTS #undef RUN_TEST
--- a/test/test_arith.h Tue Nov 19 15:55:01 2024 -0500 +++ b/test/test_arith.h Wed Nov 20 04:10:37 2024 -0500 @@ -69,7 +69,24 @@ CREATE_TESTS_SIGN(, d, , bits, size) \ CREATE_TESTS_SIGN(u, u, U, bits, size) +CREATE_TESTS(8, 8) +CREATE_TESTS(16, 4) +CREATE_TESTS(32, 2) + CREATE_TESTS(8, 16) +CREATE_TESTS(16, 8) +CREATE_TESTS(32, 4) +CREATE_TESTS(64, 2) + +CREATE_TESTS(8, 32) +CREATE_TESTS(16, 16) +CREATE_TESTS(32, 8) +CREATE_TESTS(64, 4) + +CREATE_TESTS(8, 64) +CREATE_TESTS(16, 32) +CREATE_TESTS(32, 16) +CREATE_TESTS(64, 8) #undef CREATE_TESTS_SIGN #undef CREATE_TESTS @@ -109,7 +126,24 @@ RUN_TESTS_SIGN( , bits, size) \ RUN_TESTS_SIGN(u, bits, size) + RUN_TESTS(8, 8) + RUN_TESTS(16, 4) + RUN_TESTS(32, 2) + RUN_TESTS(8, 16) + RUN_TESTS(16, 8) + RUN_TESTS(32, 4) + RUN_TESTS(64, 2) + + RUN_TESTS(8, 32) + RUN_TESTS(16, 16) + RUN_TESTS(32, 8) + RUN_TESTS(64, 4) + + RUN_TESTS(8, 64) + RUN_TESTS(16, 32) + RUN_TESTS(32, 16) + RUN_TESTS(64, 8) #undef RUN_TESTS_SIGN #undef RUN_TESTS
--- a/test/test_compare.h Tue Nov 19 15:55:01 2024 -0500 +++ b/test/test_compare.h Wed Nov 20 04:10:37 2024 -0500 @@ -32,7 +32,24 @@ #define CREATE_TESTS(bits, size) CREATE_TESTS_SIGN(, d, bits, size) CREATE_TESTS_SIGN(u, u, bits, size) +CREATE_TESTS(8, 8) +CREATE_TESTS(16, 4) +CREATE_TESTS(32, 2) + CREATE_TESTS(8, 16) +CREATE_TESTS(16, 8) +CREATE_TESTS(32, 4) +CREATE_TESTS(64, 2) + +CREATE_TESTS(8, 32) +CREATE_TESTS(16, 16) +CREATE_TESTS(32, 8) +CREATE_TESTS(64, 4) + +CREATE_TESTS(8, 64) +CREATE_TESTS(16, 32) +CREATE_TESTS(32, 16) +CREATE_TESTS(64, 8) #undef CREATE_TESTS_SIGN #undef CREATE_TESTS @@ -59,7 +76,24 @@ RUN_TESTS_SIGN( , bits, size) \ RUN_TESTS_SIGN(u, bits, size) + RUN_TESTS(8, 8) + RUN_TESTS(16, 4) + RUN_TESTS(32, 2) + RUN_TESTS(8, 16) + RUN_TESTS(16, 8) + RUN_TESTS(32, 4) + RUN_TESTS(64, 2) + + RUN_TESTS(8, 32) + RUN_TESTS(16, 16) + RUN_TESTS(32, 8) + RUN_TESTS(64, 4) + + RUN_TESTS(8, 64) + RUN_TESTS(16, 32) + RUN_TESTS(32, 16) + RUN_TESTS(64, 8) #undef RUN_TESTS_SIGN #undef RUN_TESTS