summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--src/util/meson.build1
-rw-r--r--src/util/tests/fast_idiv_by_const/Makefile.am43
-rw-r--r--src/util/tests/fast_idiv_by_const/fast_idiv_by_const_test.cpp472
-rw-r--r--src/util/tests/fast_idiv_by_const/meson.build30
5 files changed, 547 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 34689826c98..520948b0518 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3195,6 +3195,7 @@ AC_CONFIG_FILES([Makefile
src/mesa/main/tests/Makefile
src/mesa/state_tracker/tests/Makefile
src/util/Makefile
+ src/util/tests/fast_idiv_by_const/Makefile
src/util/tests/hash_table/Makefile
src/util/tests/set/Makefile
src/util/tests/string_buffer/Makefile
diff --git a/src/util/meson.build b/src/util/meson.build
index cdbad98e7cb..49d84c16ebe 100644
--- a/src/util/meson.build
+++ b/src/util/meson.build
@@ -170,6 +170,7 @@ if with_tests
)
)
+ subdir('tests/fast_idiv_by_const')
subdir('tests/hash_table')
subdir('tests/string_buffer')
subdir('tests/vma')
diff --git a/src/util/tests/fast_idiv_by_const/Makefile.am b/src/util/tests/fast_idiv_by_const/Makefile.am
new file mode 100644
index 00000000000..1ebee09f59b
--- /dev/null
+++ b/src/util/tests/fast_idiv_by_const/Makefile.am
@@ -0,0 +1,43 @@
+# Copyright © 2018 Intel
+#
+# 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 (including the next
+# paragraph) 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.
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/src/gallium/include \
+ -I$(top_srcdir)/src/gtest/include \
+ $(PTHREAD_CFLAGS) \
+ $(DEFINES)
+
+TESTS = fast_idiv_by_const_test
+
+check_PROGRAMS = $(TESTS)
+
+fast_idiv_by_const_test_SOURCES = \
+ fast_idiv_by_const_test.cpp
+
+fast_idiv_by_const_test_LDADD = \
+ $(top_builddir)/src/gtest/libgtest.la \
+ $(top_builddir)/src/util/libmesautil.la \
+ $(PTHREAD_LIBS) \
+ $(DLOPEN_LIBS)
+
+EXTRA_DIST = meson.build
diff --git a/src/util/tests/fast_idiv_by_const/fast_idiv_by_const_test.cpp b/src/util/tests/fast_idiv_by_const/fast_idiv_by_const_test.cpp
new file mode 100644
index 00000000000..3983a39edda
--- /dev/null
+++ b/src/util/tests/fast_idiv_by_const/fast_idiv_by_const_test.cpp
@@ -0,0 +1,472 @@
+/*
+ * Copyright © 2018 Intel Corporation
+ *
+ * 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 (including the next
+ * paragraph) 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 <gtest/gtest.h>
+#include "util/bigmath.h"
+#include "util/fast_idiv_by_const.h"
+#include "util/u_math.h"
+
+#define RAND_TEST_ITERATIONS 100000
+
+#define MAX_UINT(bits) \
+ (((bits) == 64) ? UINT64_MAX : ((1ull << (bits)) - 1))
+
+static inline uint64_t
+utrunc(uint64_t x, unsigned num_bits)
+{
+ if (num_bits == 64)
+ return x;
+
+ return (x << (64 - num_bits)) >> (64 - num_bits);
+}
+
+static inline int64_t
+strunc(int64_t x, unsigned num_bits)
+{
+ if (num_bits == 64)
+ return x;
+
+ return (x << (64 - num_bits)) >> (64 - num_bits);
+}
+
+static inline bool
+uint_is_in_range(uint64_t x, unsigned num_bits)
+{
+ if (num_bits == 64)
+ return true;
+
+ return x < (1ull << num_bits);
+}
+
+static inline bool
+sint_is_in_range(int64_t x, unsigned num_bits)
+{
+ if (num_bits == 64)
+ return true;
+
+ return x >= -(1ll << (num_bits - 1)) &&
+ x < (1ll << (num_bits - 1));
+}
+
+static inline uint64_t
+uadd_sat(uint64_t a, uint64_t b, unsigned num_bits)
+{
+ assert(uint_is_in_range(a, num_bits));
+ assert(uint_is_in_range(b, num_bits));
+
+ uint64_t sum = a + b;
+ if (num_bits == 64) {
+ /* Standard overflow check */
+ return sum < a ? UINT64_MAX : sum;
+ } else {
+ /* Check if sum is more than num_bits */
+ return (sum >> num_bits) ? MAX_UINT(num_bits) : sum;
+ }
+}
+
+static inline uint64_t
+umul_add_high(uint64_t a, uint64_t b, uint64_t c, unsigned num_bits)
+{
+ assert(uint_is_in_range(a, num_bits));
+ assert(uint_is_in_range(b, num_bits));
+ assert(uint_is_in_range(c, num_bits));
+
+ if (num_bits == 64) {
+ uint32_t a32[2] = { (uint32_t)a, (uint32_t)(a >> 32) };
+ uint32_t b32[2] = { (uint32_t)b, (uint32_t)(b >> 32) };
+ uint32_t c32[2] = { (uint32_t)c, (uint32_t)(c >> 32) };
+
+ uint32_t ab32[4];
+ ubm_mul_u32arr(ab32, a32, b32);
+
+ uint32_t abc32[4];
+ ubm_add_u32arr(abc32, ab32, c32);
+
+ return abc32[2] | ((uint64_t)abc32[3] << 32);
+ } else {
+ assert(num_bits <= 32);
+ return utrunc(((a * b) + c) >> num_bits, num_bits);
+ }
+}
+
+static inline int64_t
+smul_high(int64_t a, int64_t b, unsigned num_bits)
+{
+ assert(sint_is_in_range(a, num_bits));
+ assert(sint_is_in_range(b, num_bits));
+
+ if (num_bits == 64) {
+ uint32_t a32[4] = {
+ (uint32_t)a,
+ (uint32_t)(a >> 32),
+ (uint32_t)(a >> 63), /* sign extend */
+ (uint32_t)(a >> 63), /* sign extend */
+ };
+ uint32_t b32[4] = {
+ (uint32_t)b,
+ (uint32_t)(b >> 32),
+ (uint32_t)(b >> 63), /* sign extend */
+ (uint32_t)(b >> 63), /* sign extend */
+ };
+
+ uint32_t ab32[4];
+ ubm_mul_u32arr(ab32, a32, b32);
+
+ return ab32[2] | ((uint64_t)ab32[3] << 32);
+ } else {
+ assert(num_bits <= 32);
+ return strunc((a * b) >> num_bits, num_bits);
+ }
+}
+
+static inline uint64_t
+fast_udiv_add_sat(uint64_t n, struct util_fast_udiv_info m, unsigned num_bits)
+{
+ assert(uint_is_in_range(n, num_bits));
+ assert(uint_is_in_range(m.multiplier, num_bits));
+
+ n = n >> m.pre_shift;
+ n = uadd_sat(n, m.increment, num_bits);
+ n = umul_add_high(n, m.multiplier, 0, num_bits);
+ n = n >> m.post_shift;
+
+ return n;
+}
+
+static inline uint64_t
+fast_udiv_mul_add(uint64_t n, struct util_fast_udiv_info m, unsigned num_bits)
+{
+ assert(uint_is_in_range(n, num_bits));
+ assert(uint_is_in_range(m.multiplier, num_bits));
+
+ n = n >> m.pre_shift;
+ n = umul_add_high(n, m.multiplier,
+ m.increment ? m.multiplier : 0,
+ num_bits);
+ n = n >> m.post_shift;
+
+ return n;
+}
+
+static inline uint64_t
+fast_sdiv(int64_t n, int64_t d, struct util_fast_sdiv_info m, unsigned num_bits)
+{
+ assert(sint_is_in_range(n, num_bits));
+ assert(sint_is_in_range(d, num_bits));
+ assert(sint_is_in_range(m.multiplier, num_bits));
+
+ int64_t res;
+ res = smul_high(n, m.multiplier, num_bits);
+ if (d > 0 && m.multiplier < 0)
+ res = strunc(res + n, num_bits);
+ if (d < 0 && m.multiplier > 0)
+ res = strunc(res - n, num_bits);
+ res = res >> m.shift;
+ res = res - (res >> (num_bits - 1));
+
+ return res;
+}
+
+static uint64_t
+rand_uint(unsigned bits, unsigned min)
+{
+ assert(bits >= 4);
+
+ /* Make sure we get some small and large numbers and powers of two every
+ * once in a while
+ */
+ int k = rand() % 64;
+ if (k == 17) {
+ return min + (rand() % 16);
+ } else if (k == 42) {
+ return MAX_UINT(bits) - (rand() % 16);
+ } else if (k == 9) {
+ uint64_t r;
+ do {
+ r = 1ull << (rand() % bits);
+ } while (r < min);
+ return r;
+ }
+
+ if (min == 0) {
+ assert(bits <= 64);
+ uint64_t r = 0;
+ for (unsigned i = 0; i < 8; i++)
+ r |= ((uint64_t)rand() & 0xf) << i * 8;
+ return r >> (63 - (rand() % bits));
+ } else {
+ uint64_t r;
+ do {
+ r = rand_uint(bits, 0);
+ } while (r < min);
+ return r;
+ }
+}
+
+static int64_t
+rand_sint(unsigned bits, unsigned min_abs)
+{
+ /* Make sure we hit MIN_INT every once in a while */
+ if (rand() % 64 == 37)
+ return -(1 << (bits - 1));
+
+ int64_t s = rand_uint(bits - 1, min_abs);
+ return rand() & 1 ? s : -s;
+}
+
+static uint64_t
+udiv(uint64_t a, uint64_t b, unsigned bit_size)
+{
+ switch (bit_size) {
+ case 64: return (uint64_t)a / (uint64_t)b;
+ case 32: return (uint32_t)a / (uint32_t)b;
+ case 16: return (uint16_t)a / (uint16_t)b;
+ case 8: return (uint8_t)a / (uint8_t)b;
+ default:
+ assert(!"Invalid bit size");
+ return 0;
+ }
+}
+
+static int64_t
+sdiv(int64_t a, int64_t b, unsigned bit_size)
+{
+ switch (bit_size) {
+ case 64: return (int64_t)a / (int64_t)b;
+ case 32: return (int32_t)a / (int32_t)b;
+ case 16: return (int16_t)a / (int16_t)b;
+ case 8: return (int8_t)a / (int8_t)b;
+ default:
+ assert(!"Invalid bit size");
+ return 0;
+ }
+}
+
+static void
+random_udiv_add_sat_test(unsigned bits, bool bounded)
+{
+ for (unsigned i = 0; i < RAND_TEST_ITERATIONS; i++) {
+ uint64_t n = rand_uint(bits, 0);
+ uint64_t d = rand_uint(bits, 2);
+ assert(uint_is_in_range(n, bits));
+ assert(uint_is_in_range(d, bits) && d >= 2);
+
+ unsigned n_bits = bounded ? util_logbase2_64(MAX2(n, 1)) + 1 : bits;
+
+ struct util_fast_udiv_info m =
+ util_compute_fast_udiv_info(d, n_bits, bits);
+ EXPECT_EQ(fast_udiv_add_sat(n, m, bits), udiv(n, d, bits));
+ }
+}
+
+static void
+random_udiv_mul_add_test(unsigned bits, bool bounded)
+{
+ for (unsigned i = 0; i < RAND_TEST_ITERATIONS; i++) {
+ uint64_t n = rand_uint(bits, 0);
+ uint64_t d = rand_uint(bits, 1);
+ assert(uint_is_in_range(n, bits));
+ assert(uint_is_in_range(d, bits) && d >= 1);
+
+ unsigned n_bits = bounded ? util_logbase2_64(MAX2(n, 1)) + 1: bits;
+
+ struct util_fast_udiv_info m =
+ util_compute_fast_udiv_info(d, n_bits, bits);
+ EXPECT_EQ(fast_udiv_mul_add(n, m, bits), udiv(n, d, bits));
+ }
+}
+
+static void
+random_sdiv_test(unsigned bits)
+{
+ for (unsigned i = 0; i < RAND_TEST_ITERATIONS; i++) {
+ int64_t n = rand_sint(bits, 0);
+ int64_t d;
+ do {
+ d = rand_sint(bits, 2);
+ } while (util_is_power_of_two_or_zero64(llabs(d)));
+
+ assert(sint_is_in_range(n, bits));
+ assert(sint_is_in_range(d, bits) && llabs(d) >= 2);
+
+ struct util_fast_sdiv_info m =
+ util_compute_fast_sdiv_info(d, bits);
+ EXPECT_EQ(fast_sdiv(n, d, m, bits), sdiv(n, d, bits));
+ }
+}
+
+TEST(fast_idiv_by_const, uint8_add_sat)
+{
+ /* 8-bit is small enough we can brute-force the entire space */
+ for (unsigned d = 2; d < 256; d++) {
+ for (unsigned n_bits = 1; n_bits <= 8; n_bits++) {
+ struct util_fast_udiv_info m =
+ util_compute_fast_udiv_info(d, n_bits, 8);
+
+ for (unsigned n = 0; n < (1u << n_bits); n++)
+ EXPECT_EQ(fast_udiv_add_sat(n, m, 8), udiv(n, d, 8));
+ }
+ }
+}
+
+TEST(fast_idiv_by_const, uint8_mul_add)
+{
+ /* 8-bit is small enough we can brute-force the entire space */
+ for (unsigned d = 2; d < 256; d++) {
+ for (unsigned n_bits = 1; n_bits <= 8; n_bits++) {
+ struct util_fast_udiv_info m =
+ util_compute_fast_udiv_info(d, n_bits, 8);
+
+ for (unsigned n = 0; n < (1u << n_bits); n++)
+ EXPECT_EQ(fast_udiv_mul_add(n, m, 8), udiv(n, d, 8));
+ }
+ }
+}
+
+TEST(fast_idiv_by_const, int8)
+{
+ /* 8-bit is small enough we can brute-force the entire space */
+ for (int n = -128; n < 128; n++) {
+ for (int d = -128; d < 128; d++) {
+ if (util_is_power_of_two_or_zero(abs(d)))
+ continue;
+
+ struct util_fast_sdiv_info m =
+ util_compute_fast_sdiv_info(d, 8);
+ EXPECT_EQ(fast_sdiv(n, d, m, 8), sdiv(n, d, 8));
+ }
+ }
+}
+
+TEST(fast_idiv_by_const, uint16_add_sat_bounded)
+{
+ random_udiv_add_sat_test(16, true);
+}
+
+TEST(fast_idiv_by_const, uint16_add_sat_full)
+{
+ random_udiv_add_sat_test(16, false);
+}
+
+TEST(fast_idiv_by_const, uint16_mul_add_bounded)
+{
+ random_udiv_mul_add_test(16, true);
+}
+
+TEST(fast_idiv_by_const, uint16_mul_add_full)
+{
+ random_udiv_mul_add_test(16, false);
+}
+
+TEST(fast_idiv_by_const, int16)
+{
+ random_sdiv_test(16);
+}
+
+TEST(fast_idiv_by_const, uint32_add_sat_bounded)
+{
+ random_udiv_add_sat_test(32, true);
+}
+
+TEST(fast_idiv_by_const, uint32_add_sat_full)
+{
+ random_udiv_add_sat_test(32, false);
+}
+
+TEST(fast_idiv_by_const, uint32_mul_add_bounded)
+{
+ random_udiv_mul_add_test(32, true);
+}
+
+TEST(fast_idiv_by_const, uint32_mul_add_full)
+{
+ random_udiv_mul_add_test(32, false);
+}
+
+TEST(fast_idiv_by_const, int32)
+{
+ random_sdiv_test(32);
+}
+
+TEST(fast_idiv_by_const, util_fast_udiv32)
+{
+ for (unsigned i = 0; i < RAND_TEST_ITERATIONS; i++) {
+ uint32_t n = rand_uint(32, 0);
+ uint32_t d = rand_uint(32, 1);
+
+ struct util_fast_udiv_info m =
+ util_compute_fast_udiv_info(d, 32, 32);
+ EXPECT_EQ(util_fast_udiv32(n, m), n / d);
+ }
+}
+
+TEST(fast_idiv_by_const, util_fast_udiv32_nuw)
+{
+ for (unsigned i = 0; i < RAND_TEST_ITERATIONS; i++) {
+ uint32_t n = rand_uint(32, 0);
+ if (n == UINT32_MAX)
+ continue;
+ uint32_t d = rand_uint(32, 1);
+
+ struct util_fast_udiv_info m =
+ util_compute_fast_udiv_info(d, 32, 32);
+ EXPECT_EQ(util_fast_udiv32_nuw(n, m), n / d);
+ }
+}
+
+TEST(fast_idiv_by_const, util_fast_udiv32_u31_d_not_one)
+{
+ for (unsigned i = 0; i < RAND_TEST_ITERATIONS; i++) {
+ uint32_t n = rand_uint(31, 0);
+ uint32_t d = rand_uint(31, 2);
+
+ struct util_fast_udiv_info m =
+ util_compute_fast_udiv_info(d, 31, 32);
+ EXPECT_EQ(util_fast_udiv32_u31_d_not_one(n, m), n / d);
+ }
+}
+
+TEST(fast_idiv_by_const, uint64_add_sat_bounded)
+{
+ random_udiv_add_sat_test(64, true);
+}
+
+TEST(fast_idiv_by_const, uint64_add_sat_full)
+{
+ random_udiv_add_sat_test(64, false);
+}
+
+TEST(fast_idiv_by_const, uint64_mul_add_bounded)
+{
+ random_udiv_mul_add_test(64, true);
+}
+
+TEST(fast_idiv_by_const, uint64_mul_add_full)
+{
+ random_udiv_mul_add_test(64, false);
+}
+
+TEST(fast_idiv_by_const, int64)
+{
+ random_sdiv_test(64);
+}
diff --git a/src/util/tests/fast_idiv_by_const/meson.build b/src/util/tests/fast_idiv_by_const/meson.build
new file mode 100644
index 00000000000..8c3b79493ff
--- /dev/null
+++ b/src/util/tests/fast_idiv_by_const/meson.build
@@ -0,0 +1,30 @@
+# Copyright © 2018 Intel Corporation
+
+# 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.
+
+test(
+ 'fast_idiv_by_const',
+ executable(
+ 'fast_idiv_by_const_test',
+ 'fast_idiv_by_const_test.cpp',
+ dependencies : [dep_thread, dep_dl, idep_gtest],
+ include_directories : inc_common,
+ link_with : [libmesa_util],
+ )
+)