Skip to content

Commit 60c15d9

Browse files
jtl06ridiculousfish
authored andcommitted
Add ARM SVE support
1 parent 952dceb commit 60c15d9

8 files changed

Lines changed: 699 additions & 11 deletions

File tree

CMakeLists.txt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ set(LIBDIVIDE_SSE2 AUTO CACHE STRING "Enable SSE2 vector instructions")
4545
set(LIBDIVIDE_AVX2 AUTO CACHE STRING "Enable AVX2 vector instructions")
4646
set(LIBDIVIDE_AVX512 AUTO CACHE STRING "Enable AVX512 vector instructions")
4747
set(LIBDIVIDE_NEON AUTO CACHE STRING "Enable ARM NEON vector instructions")
48+
set(LIBDIVIDE_SVE AUTO CACHE STRING "Enable ARM SVE vector instructions")
4849

4950
# By default enable release mode ###############################
5051

@@ -75,7 +76,7 @@ if (WARN_VEC_CONVERSIONS)
7576
endif()
7677

7778

78-
# Check if x86/x64 CPU ########################################
79+
# Check native CPU family ######################################
7980

8081
# Note that check_cxx_source_runs() must not be used when
8182
# cross-compiling otherwise the following error will occur:
@@ -176,6 +177,23 @@ else()
176177
endif()
177178
endif()
178179

180+
# SVE
181+
if (NOT LIBDIVIDE_SVE STREQUAL "AUTO")
182+
set(LIBDIVIDE_SVE_ENABLED "${LIBDIVIDE_SVE}")
183+
else()
184+
set(SVE_TEST "
185+
#include <arm_sve.h>
186+
int main()
187+
{
188+
return svcntd() == 0;
189+
}")
190+
if (CMAKE_CROSSCOMPILING)
191+
check_cxx_source_compiles("${SVE_TEST}" LIBDIVIDE_SVE_ENABLED)
192+
else()
193+
check_cxx_source_runs("${SVE_TEST}" LIBDIVIDE_SVE_ENABLED)
194+
endif()
195+
endif()
196+
179197
# AVX512
180198
if (NOT LIBDIVIDE_AVX512 STREQUAL "AUTO")
181199
set(LIBDIVIDE_AVX512_ENABLED "${LIBDIVIDE_AVX512}")
@@ -226,6 +244,9 @@ set(LIBDIVIDE_VECTOR_EXT "")
226244
if(LIBDIVIDE_NEON_ENABLED)
227245
list(APPEND LIBDIVIDE_VECTOR_EXT "LIBDIVIDE_NEON")
228246
endif()
247+
if(LIBDIVIDE_SVE_ENABLED)
248+
list(APPEND LIBDIVIDE_VECTOR_EXT "LIBDIVIDE_SVE")
249+
endif()
229250
if(LIBDIVIDE_AVX512_ENABLED)
230251
list(APPEND LIBDIVIDE_VECTOR_EXT "LIBDIVIDE_AVX512")
231252
endif()

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
```libdivide.h``` is a header-only C/C++ library for optimizing integer division. Integer division is one of the slowest instructions on most CPUs e.g. on current x64 CPUs a 64-bit integer division has a latency of up to 90 clock cycles whereas a multiplication has a latency of only 3 clock cycles. libdivide allows you to replace expensive integer division instructions by a sequence of shift, add and multiply instructions that will calculate the integer division much faster.
77

8-
On current CPUs you can get a **speedup of up to 10x** for 64-bit integer division and a speedup of up to to 5x for 32-bit integer division when using libdivide. libdivide also supports [SSE2](https://en.wikipedia.org/wiki/SSE2), [AVX2](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions) and [AVX512](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions) vector division which provides an even larger speedup. You can test how much speedup you can achieve on your CPU using the [benchmark](#benchmark-program) program.
8+
On current CPUs you can get a **speedup of up to 10x** for 64-bit integer division and a speedup of up to 5x for 32-bit integer division when using libdivide. libdivide also supports [SSE2](https://en.wikipedia.org/wiki/SSE2), [AVX2](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions), [AVX512](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions), NEON, and SVE vector division, which can provide an even larger speedup. You can test how much speedup you can achieve on your CPU using the [benchmark](#benchmark-program) program.
99

1010
libdivide is compatible with 8-bit microcontrollers, such as the AVR series: [the CI build includes a AtMega2560 target](test/avr/readme.md). Since low end hardware such as this often do not include a hardware divider, libdivide is particularly useful. In addition to the runtime [C](doc/C-API.md) & [C++](doc/CPP-API.md) APIs, a set of [predefined macros](constant_fast_div.h) and [templates](constant_fast_div.hpp) is included to speed up division by 16-bit constants: division by a 16-bit constant is [not optimized by avr-gcc on 8-bit systems](https://stackoverflow.com/questions/47994933/why-doesnt-gcc-or-clang-on-arm-use-division-by-invariant-integers-using-multip).
1111

@@ -106,9 +106,9 @@ Caveats of branchfree divider:
106106
## Vector division
107107

108108
libdivide supports [SSE2](https://en.wikipedia.org/wiki/SSE2),
109-
[AVX2](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions) and
110-
[AVX512](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions)
111-
vector division on x86 and x64 CPUs. In the example below we divide the packed 32-bit integers inside an AVX512 vector using libdivide. libdivide supports 32-bit and 64-bit vector division for both signed and unsigned integers.
109+
[AVX2](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions),
110+
[AVX512](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions),
111+
NEON, and SVE vector division. In the example below we divide the packed 32-bit integers inside an AVX512 vector using libdivide. libdivide supports 16-bit, 32-bit, and 64-bit vector division for both signed and unsigned integers.
112112

113113
```C++
114114
#include "libdivide.h"
@@ -129,6 +129,7 @@ Note that you need to define one of macros below to enable vector division:
129129
* ```LIBDIVIDE_AVX2```
130130
* ```LIBDIVIDE_AVX512```
131131
* ```LIBDIVIDE_NEON```
132+
* ```LIBDIVIDE_SVE```
132133
133134
## Performance Tips
134135
@@ -138,9 +139,9 @@ Note that you need to define one of macros below to enable vector division:
138139
choose the one that performs best. The branchfree divider is more likely to get auto
139140
vectorized by the compiler (if you compile with e.g. ```-march=native```). But don't forget
140141
that the unsigned branchfree divider cannot be 1.
141-
* Vector division is much faster for 32-bit than for 64-bit. This is because there are
142-
currently no vector multiplication instructions on x86 to efficiently calculate
143-
64-bit * 64-bit to 128-bit.
142+
* On x86, vector division is much faster for 32-bit than for 64-bit. This is because
143+
there are currently no vector multiplication instructions on x86 to efficiently
144+
calculate 64-bit * 64-bit to 128-bit.
144145
145146
## Build instructions
146147

doc/C-API.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,29 @@ int64x2_t libdivide_s64_branchfree_do_vec128(int64x2_t numers, const struct libd
6262
6363
You need to define ```LIBDIVIDE_NEON``` to enable NEON vector division.
6464
65+
## libdivide SVE vector division
66+
67+
```C
68+
/* libdivide SVE division */
69+
svuint16_t libdivide_u16_do_sve(svuint16_t numers, const struct libdivide_u16_t *denom);
70+
svint16_t libdivide_s16_do_sve(svint16_t numers, const struct libdivide_s16_t *denom);
71+
svuint32_t libdivide_u32_do_sve(svuint32_t numers, const struct libdivide_u32_t *denom);
72+
svint32_t libdivide_s32_do_sve(svint32_t numers, const struct libdivide_s32_t *denom);
73+
svuint64_t libdivide_u64_do_sve(svuint64_t numers, const struct libdivide_u64_t *denom);
74+
svint64_t libdivide_s64_do_sve(svint64_t numers, const struct libdivide_s64_t *denom);
75+
76+
/* libdivide SVE branchfree division */
77+
svuint16_t libdivide_u16_branchfree_do_sve(svuint16_t numers, const struct libdivide_u16_branchfree_t *denom);
78+
svint16_t libdivide_s16_branchfree_do_sve(svint16_t numers, const struct libdivide_s16_branchfree_t *denom);
79+
svuint32_t libdivide_u32_branchfree_do_sve(svuint32_t numers, const struct libdivide_u32_branchfree_t *denom);
80+
svint32_t libdivide_s32_branchfree_do_sve(svint32_t numers, const struct libdivide_s32_branchfree_t *denom);
81+
svuint64_t libdivide_u64_branchfree_do_sve(svuint64_t numers, const struct libdivide_u64_branchfree_t *denom);
82+
svint64_t libdivide_s64_branchfree_do_sve(svint64_t numers, const struct libdivide_s64_branchfree_t *denom);
83+
```
84+
85+
You need to define ```LIBDIVIDE_SVE``` and compile for a target with Arm SVE
86+
support to enable SVE vector division.
87+
6588
## libdivide SSE2 vector division
6689

6790
```C

doc/CPP-API.md

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,53 @@ template <Branching ALGO>
7777
int64x2_t operator/=(int64x2_t &n, const divider<int64_t, ALGO> &div)
7878
```
7979

80-
You need to define ```LIBDIVIDE_NEON``` to enable SSE2 vector division.
80+
You need to define ```LIBDIVIDE_NEON``` to enable NEON vector division.
8181

82+
## SVE vector division
83+
84+
```C++
85+
// Overload of operator /
86+
template <Branching ALGO>
87+
svuint16_t operator/(svuint16_t n, const divider<uint16_t, ALGO> &div)
88+
89+
template <Branching ALGO>
90+
svint16_t operator/(svint16_t n, const divider<int16_t, ALGO> &div)
91+
92+
template <Branching ALGO>
93+
svuint32_t operator/(svuint32_t n, const divider<uint32_t, ALGO> &div)
94+
95+
template <Branching ALGO>
96+
svint32_t operator/(svint32_t n, const divider<int32_t, ALGO> &div)
97+
98+
template <Branching ALGO>
99+
svuint64_t operator/(svuint64_t n, const divider<uint64_t, ALGO> &div)
100+
101+
template <Branching ALGO>
102+
svint64_t operator/(svint64_t n, const divider<int64_t, ALGO> &div)
103+
104+
105+
// Overload of operator /=
106+
template <Branching ALGO>
107+
svuint16_t operator/=(svuint16_t &n, const divider<uint16_t, ALGO> &div)
108+
109+
template <Branching ALGO>
110+
svint16_t operator/=(svint16_t &n, const divider<int16_t, ALGO> &div)
111+
112+
template <Branching ALGO>
113+
svuint32_t operator/=(svuint32_t &n, const divider<uint32_t, ALGO> &div)
114+
115+
template <Branching ALGO>
116+
svint32_t operator/=(svint32_t &n, const divider<int32_t, ALGO> &div)
117+
118+
template <Branching ALGO>
119+
svuint64_t operator/=(svuint64_t &n, const divider<uint64_t, ALGO> &div)
120+
121+
template <Branching ALGO>
122+
svint64_t operator/=(svint64_t &n, const divider<int64_t, ALGO> &div)
123+
```
124+
125+
You need to define ```LIBDIVIDE_SVE``` and compile for a target with Arm SVE
126+
support to enable SVE vector division.
82127

83128
## SSE2 vector division
84129

0 commit comments

Comments
 (0)