From: Jacob Lifshay Date: Tue, 13 Jun 2017 04:07:36 +0000 (-0700) Subject: added util::bitset and working on implementing generate_spirv_parser X-Git-Tag: gsoc-2017~102 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=18df474ccf8c8d586c083be887acec58deacdfed;p=kazan.git added util::bitset and working on implementing generate_spirv_parser --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a4c3944..ddee658 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,7 @@ # SOFTWARE. # cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(spirv) add_subdirectory(demo) add_subdirectory(json) diff --git a/src/demo/demo.cpp b/src/demo/demo.cpp index 601571f..2ef3616 100644 --- a/src/demo/demo.cpp +++ b/src/demo/demo.cpp @@ -26,8 +26,8 @@ #include #include #include -#include "../spirv/spirv.h" -#include "../util/optional.h" +#include "spirv/spirv.h" +#include "util/optional.h" namespace vulkan_cpu { diff --git a/src/generate_spirv_parser/ast.h b/src/generate_spirv_parser/ast.h index 3228746..81e650e 100644 --- a/src/generate_spirv_parser/ast.h +++ b/src/generate_spirv_parser/ast.h @@ -24,7 +24,7 @@ #ifndef GENERATE_SPIRV_PARSER_AST_H_ #define GENERATE_SPIRV_PARSER_AST_H_ -#include "../json/json.h" +#include "json/json.h" #include #include #include diff --git a/src/generate_spirv_parser/generate.cpp b/src/generate_spirv_parser/generate.cpp index b39259c..9d1f8f7 100644 --- a/src/generate_spirv_parser/generate.cpp +++ b/src/generate_spirv_parser/generate.cpp @@ -253,7 +253,8 @@ struct Spirv_header_generator final : public Generator state.open_output_file(); write_file_comments(state, top_level.copyright); write_file_guard_start(state); - state << "#include \n"; + state << "#include \n" + "#include \"util/bitset.h\"\n"; state << "\n"; write_namespaces_start(state, spirv_namespace_names); state << "typedef std::uint32_t Word;\n"; @@ -272,7 +273,7 @@ struct Spirv_header_generator final : public Generator extensions_list.push_back(extension); std::sort(extensions_list.begin(), extensions_list.end()); state << "\n" - "enum class Extension\n" + "enum class Extension : std::size_t\n" "{\n"; { auto push_indent = state.pushed_indent(); @@ -280,6 +281,10 @@ struct Spirv_header_generator final : public Generator state << indent << extension << ",\n"; } state << "};\n" + "\n" + "constexpr std::size_t Extension_count = " + << unsigned_dec_integer_literal(extensions_list.size()) + << ";\n" "\n" "constexpr const char *get_extension_name(Extension extension) noexcept\n" "{\n"; @@ -308,10 +313,8 @@ struct Spirv_header_generator final : public Generator }); for(auto *operand_kind : operand_kinds) { - switch(operand_kind->category) - { - case ast::Operand_kinds::Operand_kind::Category::value_enum: - case ast::Operand_kinds::Operand_kind::Category::bit_enum: + if(util::holds_alternative( + operand_kind->value)) { auto &enumerants = util::get(operand_kind->value); @@ -332,9 +335,6 @@ struct Spirv_header_generator final : public Generator } push_indent.finish(); state << "};\n"; - break; - } -#warning finish } } @@ -351,4 +351,4 @@ std::unique_ptr Generators::make_spirv_header_generator() } } } -} \ No newline at end of file +} diff --git a/src/generate_spirv_parser/generate_spirv_parser.cpp b/src/generate_spirv_parser/generate_spirv_parser.cpp index 07d371e..4a88efe 100644 --- a/src/generate_spirv_parser/generate_spirv_parser.cpp +++ b/src/generate_spirv_parser/generate_spirv_parser.cpp @@ -21,10 +21,10 @@ * */ #include -#include "../json/json.h" -#include "../json/parser.h" +#include "json/json.h" +#include "json/parser.h" #include "parser.h" -#include "../util/optional.h" +#include "util/optional.h" #include "generate.h" namespace vulkan_cpu diff --git a/src/generate_spirv_parser/parser.cpp b/src/generate_spirv_parser/parser.cpp index ee6fd29..8c96b89 100644 --- a/src/generate_spirv_parser/parser.cpp +++ b/src/generate_spirv_parser/parser.cpp @@ -21,7 +21,7 @@ * */ #include "parser.h" -#include "../util/optional.h" +#include "util/optional.h" #include #include #include diff --git a/src/generate_spirv_parser/parser.h b/src/generate_spirv_parser/parser.h index df3b9dc..acf2457 100644 --- a/src/generate_spirv_parser/parser.h +++ b/src/generate_spirv_parser/parser.h @@ -29,9 +29,9 @@ #include #include #include -#include "../util/variant.h" -#include "../json/json.h" -#include "../json/parser.h" +#include "util/variant.h" +#include "json/json.h" +#include "json/parser.h" namespace vulkan_cpu { diff --git a/src/json/json.cpp b/src/json/json.cpp index 1c41d79..28f4ae5 100644 --- a/src/json/json.cpp +++ b/src/json/json.cpp @@ -28,7 +28,7 @@ #include #include #include -#include "../util/soft_float.h" +#include "util/soft_float.h" namespace vulkan_cpu { diff --git a/src/json/json.h b/src/json/json.h index 7d9f4c0..15cbe05 100644 --- a/src/json/json.h +++ b/src/json/json.h @@ -35,8 +35,8 @@ #include #include #include -#include "../util/variant.h" -#include "../util/optional.h" +#include "util/variant.h" +#include "util/optional.h" #include "location.h" namespace vulkan_cpu diff --git a/src/json/parser.cpp b/src/json/parser.cpp index 238e302..1481c92 100644 --- a/src/json/parser.cpp +++ b/src/json/parser.cpp @@ -26,7 +26,7 @@ #include #include #include -#include "../util/soft_float.h" +#include "util/soft_float.h" namespace vulkan_cpu { diff --git a/src/json/parser.h b/src/json/parser.h index dc14771..31adb3e 100644 --- a/src/json/parser.h +++ b/src/json/parser.h @@ -32,7 +32,7 @@ #include "json.h" #include "source.h" #include "location.h" -#include "../util/optional.h" +#include "util/optional.h" namespace vulkan_cpu { diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 387804f..42d814d 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -20,6 +20,7 @@ # cmake_minimum_required(VERSION 3.1 FATAL_ERROR) set(sources bit_intrinsics.cpp + bitset.cpp copy_cv_ref.cpp in_place.cpp invoke.cpp diff --git a/src/util/bit_intrinsics.h b/src/util/bit_intrinsics.h index 4550042..3ffd6a7 100644 --- a/src/util/bit_intrinsics.h +++ b/src/util/bit_intrinsics.h @@ -30,23 +30,23 @@ #if defined(__clang__) #if defined(__apple_build_version__) #if __clang_major__ > 5 || (__clang_major__ == 5 && __clang_minor__ >= 1) -#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED 1 +#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED 1 #endif #else #if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 4) -#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED 1 +#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED 1 #endif #endif #elif defined(__INTEL_COMPILER) #warning figure out icc version numbers for constexpr __builtin_clz and __builtin_ctz #elif defined(__GNUC__) // gcc supports constexpr __builtin_clz and __builtin_ctz before it supports c++14 -#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED 1 +#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED 1 #endif #if 1 -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED -#undef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED +#undef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED #endif #endif @@ -56,7 +56,7 @@ namespace util { constexpr std::uint32_t clz4(std::uint8_t v) noexcept { -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED return v == 0 ? 4 : __builtin_clz(v) - __builtin_clz(0x8U); #else typedef const std::uint_fast8_t LookupTableType[0x10]; @@ -70,7 +70,7 @@ constexpr std::uint32_t clz4(std::uint8_t v) noexcept constexpr std::uint32_t clz8(std::uint8_t v) noexcept { -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED return v == 0 ? 8 : __builtin_clz(v) - __builtin_clz(0x80U); #else return ((v & 0xF0) == 0) ? 4 + clz4(v) : clz4(v >> 4); @@ -79,7 +79,7 @@ constexpr std::uint32_t clz8(std::uint8_t v) noexcept constexpr std::uint32_t clz16(std::uint16_t v) noexcept { -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED return v == 0 ? 16 : __builtin_clz(v) - (std::numeric_limits::digits - 16); #else return ((v & 0xFF00U) == 0) ? 8 + clz8(v) : clz8(v >> 8); @@ -88,7 +88,7 @@ constexpr std::uint32_t clz16(std::uint16_t v) noexcept constexpr std::uint32_t clz32(std::uint32_t v) noexcept { -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED return v == 0 ? 32 : __builtin_clzl(v) - (std::numeric_limits::digits - 32); #else return ((v & 0xFFFF0000UL) == 0) ? 16 + clz16(v) : clz16(v >> 16); @@ -97,7 +97,7 @@ constexpr std::uint32_t clz32(std::uint32_t v) noexcept constexpr std::uint32_t clz64(std::uint64_t v) noexcept { -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED return v == 0 ? 64 : __builtin_clzll(v) - (std::numeric_limits::digits - 64); #else return ((v & 0xFFFFFFFF00000000ULL) == 0) ? 32 + clz32(v) : clz32(v >> 32); @@ -106,7 +106,7 @@ constexpr std::uint32_t clz64(std::uint64_t v) noexcept constexpr std::uint32_t ctz4(std::uint8_t v) noexcept { -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED return v == 0 ? 4 : __builtin_ctz(v); #else typedef const std::uint_fast8_t LookupTableType[0x10]; @@ -120,7 +120,7 @@ constexpr std::uint32_t ctz4(std::uint8_t v) noexcept constexpr std::uint32_t ctz8(std::uint8_t v) noexcept { -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED return v == 0 ? 8 : __builtin_ctz(v); #else return ((v & 0xF0) == 0) ? ctz4(v) : 4 + ctz4(v >> 4); @@ -129,7 +129,7 @@ constexpr std::uint32_t ctz8(std::uint8_t v) noexcept constexpr std::uint32_t ctz16(std::uint16_t v) noexcept { -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED return v == 0 ? 16 : __builtin_ctz(v); #else return ((v & 0xFF00U) == 0) ? ctz8(v) : 8 + ctz8(v >> 8); @@ -138,7 +138,7 @@ constexpr std::uint32_t ctz16(std::uint16_t v) noexcept constexpr std::uint32_t ctz32(std::uint32_t v) noexcept { -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED return v == 0 ? 32 : __builtin_ctzl(v); #else return ((v & 0xFFFF0000UL) == 0) ? ctz16(v) : 16 + ctz16(v >> 16); @@ -147,12 +147,59 @@ constexpr std::uint32_t ctz32(std::uint32_t v) noexcept constexpr std::uint32_t ctz64(std::uint64_t v) noexcept { -#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED return v == 0 ? 64 : __builtin_ctzll(v); #else return ((v & 0xFFFFFFFF00000000ULL) == 0) ? ctz32(v) : 32 + ctz32(v >> 32); #endif } + +constexpr std::uint32_t popcount8(std::uint8_t v) noexcept +{ +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED + return __builtin_popcount(v); +#else + constexpr std::uint8_t lookup_table[0x10] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + }; + return lookup_table[v & 0xF] + lookup_table[v >> 4]; +#endif +} + +constexpr std::uint32_t popcount32(std::uint32_t v) noexcept +{ +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED + return __builtin_popcountl(v); +#else + constexpr std::uint32_t m1 = 0x5555'5555UL; + constexpr std::uint32_t m2 = 0x3333'3333UL; + constexpr std::uint32_t m4 = 0x0F0F'0F0FUL; + v -= (v >> 1) & m1; + v = (v & m2) + ((v >> 2) & m2); + v = (v & m4) + ((v >> 4) & m4); + return static_cast(v * 0x0101'0101UL) >> 24; +#endif +} + +constexpr std::uint32_t popcount16(std::uint16_t v) noexcept +{ + return popcount32(v); +} + +constexpr std::uint32_t popcount64(std::uint64_t v) noexcept +{ +#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED + return __builtin_popcountll(v); +#else + constexpr std::uint64_t m1 = 0x5555'5555'5555'5555ULL; + constexpr std::uint64_t m2 = 0x3333'3333'3333'3333ULL; + constexpr std::uint64_t m4 = 0x0F0F'0F0F'0F0F'0F0FULL; + v -= (v >> 1) & m1; + v = (v & m2) + ((v >> 2) & m2); + v = (v & m4) + ((v >> 4) & m4); + return static_cast(v * 0x0101'0101'0101'0101ULL) >> 56; +#endif +} } } diff --git a/src/util/bitset.cpp b/src/util/bitset.cpp new file mode 100644 index 0000000..73f9405 --- /dev/null +++ b/src/util/bitset.cpp @@ -0,0 +1,433 @@ +/* + * Copyright 2017 Jacob Lifshay + * + * 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. + * + */ +// derived from +// https://github.com/programmerjake/hashlife-voxels/blob/0dd91021a5b9caeffb7849b2114dca89204876bd/util/bitset.cpp + +#include "bitset.h" +#include +#include +#include +#include +#include +#include +#include + +namespace vulkan_cpu +{ +namespace util +{ +namespace detail +{ +#if 0 +#warning testing bitset +struct Bitset_nontemplate_base::Tester final +{ + template + static void check_unused_bits(const bitset &value) + { + constexpr Word_type unused_bits = + Bit_count % word_bit_count ? ~((1ULL << (Bit_count % word_bit_count)) - 1ULL) : 0; + assert((value.get_word(value.word_count - 1) & unused_bits) == 0); + } + static void check_unused_bits(const bitset<0> &) + { + } + template + static void test_default_construct() + { + bitset value; + for(std::size_t i = 0; i < value.word_count; i++) + assert(value.get_word(i) == 0); + check_unused_bits(value); + } + template + static void test_construct_from_ull() + { + for(std::size_t i = 0; i < std::numeric_limits::digits; i++) + { + bitset value(1ULL << i); + check_unused_bits(value); + assert(bitset(1ULL << i).to_ullong() == (1ULL << i) || i >= Bit_count); + } + } + template + static void test_reference_assign() + { + std::default_random_engine re; + std::uniform_int_distribution distribution; + for(std::size_t i = 0; i < 1000; i++) + { + bitset src(distribution(re)), dest; + for(std::size_t j = 0; j < Bit_count; j++) + { + dest[j] = src[j]; + check_unused_bits(src); + check_unused_bits(dest); + } + assert(src == dest); + } + } + template + static void test_reference_flip() + { + if(Bit_count == 0) + return; + std::default_random_engine re; + std::vector vector; + vector.resize(Bit_count, false); + bitset value; + for(std::size_t i = 0; i < 1000; i++) + { + std::size_t index = std::uniform_int_distribution(0, Bit_count - 1)(re); + vector[index].flip(); + value[index].flip(); + check_unused_bits(value); + for(std::size_t j = 0; j < Bit_count; j++) + assert(value[index] == static_cast(vector[index])); + } + } + template + static void test_test() + { + std::default_random_engine re; + std::vector vector; + vector.resize(Bit_count, false); + bitset value; + if(Bit_count != 0) + { + for(std::size_t i = 0; i < 1000; i++) + { + std::size_t index = + std::uniform_int_distribution(0, Bit_count - 1)(re); + vector[index].flip(); + value[index].flip(); + check_unused_bits(value); + } + } + for(std::size_t i = 0; i < Bit_count + 1000; i++) + { + bool threw = false; + bool result = false; + try + { + result = value.test(i); + } + catch(std::out_of_range &) + { + threw = true; + } + if(i >= Bit_count) + { + assert(threw); + } + else + { + assert(!threw); + assert(result == vector[i]); + } + } + } + template + static void test_all_none_any_and_count_helper(const std::vector &vector, + const bitset &value) + { + std::size_t set_bit_count = 0; + for(std::size_t i = 0; i < Bit_count; i++) + if(vector[i]) + set_bit_count++; + assert(value.all() == (set_bit_count == Bit_count)); + assert(value.any() == (set_bit_count != 0)); + assert(value.none() == (set_bit_count == 0)); + assert(value.count() == set_bit_count); + } + template + static void test_all_none_any_and_count() + { + std::default_random_engine re; + std::vector vector; + vector.resize(Bit_count, false); + bitset value; + test_all_none_any_and_count_helper(vector, value); + if(Bit_count != 0) + { + for(std::size_t i = 0; i < 1000; i++) + { + std::size_t index = + std::uniform_int_distribution(0, Bit_count - 1)(re); + vector[index].flip(); + value[index].flip(); + check_unused_bits(value); + test_all_none_any_and_count_helper(vector, value); + } + } + for(std::size_t i = 0; i < Bit_count; i++) + { + value[i] = true; + vector[i] = true; + check_unused_bits(value); + test_all_none_any_and_count_helper(vector, value); + } + } + template + static void test_and_or_and_xor_helper(const std::vector &vector1, + const std::vector &vector2, + const bitset &bitset1, + const bitset &bitset2) + { + bitset dest_bitset_and = bitset1 & bitset2; + bitset dest_bitset_or = bitset1 | bitset2; + bitset dest_bitset_xor = bitset1 ^ bitset2; + check_unused_bits(dest_bitset_and); + check_unused_bits(dest_bitset_or); + check_unused_bits(dest_bitset_xor); + for(std::size_t i = 0; i < Bit_count; i++) + { + assert(dest_bitset_and[i] == (vector1[i] && vector2[i])); + assert(dest_bitset_or[i] == (vector1[i] || vector2[i])); + assert(dest_bitset_xor[i] == (static_cast(vector1[i]) != vector2[i])); + } + } + template + static void test_and_or_and_xor() + { + std::default_random_engine re; + std::vector vector1, vector2; + vector1.resize(Bit_count, false); + vector2.resize(Bit_count, false); + bitset bitset1, bitset2; + test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2); + if(Bit_count != 0) + { + for(std::size_t i = 0; i < 2000; i++) + { + std::size_t index = + std::uniform_int_distribution(0, Bit_count * 2 - 1)(re); + bool is_second_bit_set = index >= Bit_count; + index %= Bit_count; + if(is_second_bit_set) + { + vector2[index].flip(); + bitset2[index].flip(); + } + else + { + vector1[index].flip(); + bitset1[index].flip(); + } + check_unused_bits(bitset1); + check_unused_bits(bitset2); + test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2); + } + } + for(std::size_t i = 0; i < Bit_count; i++) + { + bitset1[i] = true; + vector1[i] = true; + check_unused_bits(bitset1); + check_unused_bits(bitset2); + test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2); + } + for(std::size_t i = 0; i < Bit_count; i++) + { + bitset2[i] = true; + vector2[i] = true; + check_unused_bits(bitset1); + check_unused_bits(bitset2); + test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2); + } + } + template + static void test_not() + { + std::default_random_engine re; + std::vector vector; + vector.resize(Bit_count, false); + bitset value; + if(Bit_count != 0) + { + for(std::size_t i = 0; i < 1000; i++) + { + std::size_t index = + std::uniform_int_distribution(0, Bit_count - 1)(re); + vector[index].flip(); + value[index].flip(); + check_unused_bits(value); + bitset bitset_not = ~value; + check_unused_bits(bitset_not); + for(std::size_t j = 0; j < Bit_count; j++) + assert(vector[j] == !bitset_not[j]); + } + } + } + template + static void test_shift_helper(const std::vector &vector, const bitset &value) + { + for(std::size_t shift_count = 0; shift_count < Bit_count * 2 + 1; shift_count++) + { + bitset bitset_shifted_left = value << shift_count; + bitset bitset_shifted_right = value >> shift_count; + check_unused_bits(bitset_shifted_left); + check_unused_bits(bitset_shifted_right); + for(std::size_t i = 0; i < Bit_count; i++) + { + assert(bitset_shifted_left[i] + == (i < shift_count ? false : static_cast(vector[i - shift_count]))); + assert(bitset_shifted_right[i] == (shift_count >= Bit_count - i ? + false : + static_cast(vector[i + shift_count]))); + } + } + } + template + static void test_shift() + { + std::default_random_engine re; + std::vector vector; + vector.resize(Bit_count, false); + bitset value; + test_shift_helper(vector, value); + if(Bit_count != 0) + { + for(std::size_t i = 0; i < 1000; i++) + { + std::size_t index = + std::uniform_int_distribution(0, Bit_count - 1)(re); + vector[index].flip(); + value[index].flip(); + check_unused_bits(value); + test_shift_helper(vector, value); + } + } + for(std::size_t i = 0; i < Bit_count; i++) + { + value[i] = true; + vector[i] = true; + check_unused_bits(value); + test_shift_helper(vector, value); + } + } + template + static void test_global_set_and_reset() + { + bitset value; + value.reset(); + check_unused_bits(value); + assert(value.none()); + value.set(); + check_unused_bits(value); + assert(value.all()); + } + template + static void test_find_helper(const std::string &string, const bitset &value) + { + for(std::size_t i = 0; i < Bit_count; i++) + assert(string[i] == (value[i] ? '1' : '0')); + for(std::size_t start = 0; start <= Bit_count; start++) + { + assert(string.find_first_of('1', start) == value.find_first(true, start)); + assert(string.find_first_not_of('1', start) == value.find_first(false, start)); + assert(string.find_last_of('1', start) == value.find_last(true, start)); + assert(string.find_last_not_of('1', start) == value.find_last(false, start)); + } + } + template + static void test_find() + { + std::default_random_engine re; + std::string string; + string.resize(Bit_count, '0'); + bitset value; + test_find_helper(string, value); + if(Bit_count != 0) + { + for(std::size_t i = 0; i < 1000; i++) + { + std::size_t index = + std::uniform_int_distribution(0, Bit_count - 1)(re); + string[index] = (string[index] == '0' ? '1' : '0'); + value[index].flip(); + check_unused_bits(value); + test_find_helper(string, value); + } + } + for(std::size_t i = 0; i < Bit_count; i++) + { + value[i] = true; + string[i] = '1'; + check_unused_bits(value); + test_find_helper(string, value); + } + if(Bit_count != 0) + { + for(std::size_t i = 0; i < 1000; i++) + { + std::size_t index = + std::uniform_int_distribution(0, Bit_count - 1)(re); + string[index] = (string[index] == '0' ? '1' : '0'); + value[index].flip(); + check_unused_bits(value); + test_find_helper(string, value); + } + } + } + template + static char test() + { + std::cout << "testing bitset<" << Bit_count << ">" << std::endl; + test_default_construct(); + test_construct_from_ull(); + test_reference_assign(); + test_reference_flip(); + test_test(); + test_all_none_any_and_count(); + test_and_or_and_xor(); + test_not(); + test_shift(); + test_global_set_and_reset(); + test_find(); + return 0; + } + template + static void test_helper(Args...) + { + } + template + static void test(std::index_sequence) + { + test_helper(test()...); + } + struct Test_runner final + { + Test_runner() + { + test(std::make_index_sequence<128>()); + std::exit(0); + } + }; + static Test_runner test_runner; +}; +Bitset_nontemplate_base::Tester::Test_runner Bitset_nontemplate_base::Tester::test_runner; +#endif +} +} +} diff --git a/src/util/bitset.h b/src/util/bitset.h new file mode 100644 index 0000000..ca93b74 --- /dev/null +++ b/src/util/bitset.h @@ -0,0 +1,615 @@ +/* + * Copyright 2017 Jacob Lifshay + * + * 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 UTIL_BITSET_H_ +#define UTIL_BITSET_H_ + +#include "bit_intrinsics.h" +#include +#include +#include +#include +#include +#include +#include + +// I have my own bitset implementation because std::bitset is not completely constexpr. + +// derived from +// https://github.com/programmerjake/hashlife-voxels/blob/0dd91021a5b9caeffb7849b2114dca89204876bd/util/bitset.h + +namespace vulkan_cpu +{ +namespace util +{ +template +class bitset; +namespace detail +{ +class Bitset_nontemplate_base_helper +{ +protected: + static constexpr bool is_power_of_2(std::size_t v) noexcept + { + return (v & (v - 1)) == 0; + } +}; + +struct Bitset_nontemplate_base : public Bitset_nontemplate_base_helper +{ +protected: + struct Tester; + +public: + typedef std::uintptr_t Word_type; + static constexpr std::size_t word_bit_count = std::numeric_limits::digits; + static_assert(is_power_of_2(word_bit_count), "word_bit_count is not a power of 2"); + static constexpr std::size_t constexpr_min(std::size_t a, std::size_t b) noexcept + { + return a > b ? b : a; + } + static constexpr std::size_t get_word_index(std::size_t bit_index) noexcept + { + return bit_index / word_bit_count; + } + static constexpr Word_type get_word_mask(std::size_t bit_index) noexcept + { + return static_cast(1) << (bit_index % word_bit_count); + } + static constexpr std::size_t get_word_count(std::size_t bit_count) noexcept + { + return (bit_count + (word_bit_count - 1)) / word_bit_count; + } +}; + +template +class Bitset_base : protected Bitset_nontemplate_base +{ +protected: + static constexpr std::size_t word_count = Word_count; + Word_type words[Word_count]; // little endian order + constexpr Bitset_base() noexcept : words{} + { + } + constexpr Bitset_base(unsigned long long val) noexcept + : Bitset_base( + val, + std::make_index_sequence::digits), Word_count)>()) + { + } + constexpr Word_type get_word(std::size_t word_index) const noexcept + { + return words[word_index]; + } + constexpr void set_word(std::size_t word_index, Word_type word_value) noexcept + { + words[word_index] = word_value; + } + constexpr bool equals(const Bitset_base &rt) const noexcept + { + for(std::size_t i = 0; i < word_count; i++) + if(words[i] != rt.words[i]) + return false; + return true; + } + +private: + template + constexpr Bitset_base(unsigned long long val, std::index_sequence) noexcept + : words{ + static_cast(val >> Indexes * word_bit_count)..., + } + { + } +}; + +template <> +class Bitset_base<0> : protected Bitset_nontemplate_base +{ +protected: + static constexpr std::size_t word_count = 0; + constexpr Bitset_base() noexcept + { + } + constexpr Bitset_base(unsigned long long) noexcept + { + } + constexpr Word_type get_word(std::size_t word_index) const noexcept + { + return (static_cast(word_index), 0); + } + constexpr void set_word(std::size_t word_index, Word_type word_value) noexcept + { + static_cast(word_index); + static_cast(word_value); + } + constexpr bool equals(const Bitset_base &rt) const noexcept + { + return true; + } + +public: + constexpr unsigned long long to_ullong() const + { + return 0; + } +}; +} + +template +class bitset final + : public detail::Bitset_base +{ +private: + friend struct detail::Bitset_nontemplate_base::Tester; + static constexpr std::size_t bit_count = Bit_count; + typedef detail::Bitset_base Base; + using typename Base::Word_type; + using Base::word_count; + using Base::get_word; + using Base::set_word; + using Base::get_word_count; + using Base::get_word_mask; + using Base::get_word_index; + using Base::word_bit_count; + +private: + constexpr Word_type get_word_checked(std::size_t word_index) const noexcept + { + return word_index < word_count ? get_word(word_index) : 0; + } + +public: + constexpr bitset() noexcept : Base() + { + } + constexpr bitset(unsigned long long val) noexcept + : Base(bit_count >= std::numeric_limits::digits ? + val : + val & ((1ULL << bit_count) - 1ULL)) + { + } + class reference final + { + template + friend class bitset; + + private: + bitset *base; + std::size_t bit_index; + constexpr reference(bitset *base, std::size_t bit_index) noexcept : base(base), + bit_index(bit_index) + { + } + + public: + constexpr reference &operator=( + const reference &rt) noexcept // assigns referenced value, not this class + { + return operator=(static_cast(rt)); + } + constexpr reference &operator=(bool value) noexcept + { + auto mask = get_word_mask(bit_index); + auto word = base->get_word(get_word_index(bit_index)); + if(value) + word |= mask; + else + word &= ~mask; + base->set_word(get_word_index(bit_index), word); + return *this; + } + constexpr operator bool() const noexcept + { + return static_cast(base)->operator[](bit_index); + } + constexpr bool operator~() const noexcept + { + return !operator bool(); + } + constexpr reference &flip() noexcept + { + auto mask = get_word_mask(bit_index); + auto word = base->get_word(get_word_index(bit_index)); + word ^= mask; + base->set_word(get_word_index(bit_index), word); + return *this; + } + }; + constexpr bool operator==(const bitset &rt) const noexcept + { + return this->equals(rt); + } + constexpr bool operator!=(const bitset &rt) const noexcept + { + return !this->equals(rt); + } + constexpr reference operator[](std::size_t bit_index) noexcept + { + return reference(this, bit_index); + } + constexpr bool operator[](std::size_t bit_index) const noexcept + { + return get_word_mask(bit_index) & get_word(get_word_index(bit_index)); + } + constexpr bool test(std::size_t bit_index) const + { + if(bit_index >= bit_count) + throw std::out_of_range("bit_index out of range in bitset::test"); + return operator[](bit_index); + } + constexpr bool all() const noexcept + { + if(bit_count == 0) + return true; + for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++) + if(get_word(i) != static_cast(-1)) + return false; + return get_word(get_word_index(bit_count - 1)) + == static_cast(static_cast(get_word_mask(bit_count - 1) << 1) + - 1); + } + constexpr bool any() const noexcept + { + if(bit_count == 0) + return false; + for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++) + if(get_word(i) != 0) + return true; + return get_word(get_word_index(bit_count - 1)) != 0; + } + constexpr bool none() const noexcept + { + return !any(); + } + constexpr std::size_t count() const noexcept + { + std::size_t retval = 0; + for(std::size_t i = 0; i < word_count; i++) + { + static_assert( + std::numeric_limits::max() <= std::numeric_limits::max(), + ""); + if(std::numeric_limits::max() <= std::numeric_limits::max()) + retval += popcount32(get_word(i)); + else + retval += popcount64(get_word(i)); + } + return retval; + } + constexpr std::size_t size() const noexcept + { + return bit_count; + } + constexpr bitset &operator&=(const bitset &rt) noexcept + { + for(std::size_t i = 0; i < word_count; i++) + set_word(i, get_word(i) & rt.get_word(i)); + return *this; + } + friend constexpr bitset operator&(bitset a, const bitset &b) noexcept + { + return a &= b; + } + constexpr bitset &operator|=(const bitset &rt) noexcept + { + for(std::size_t i = 0; i < word_count; i++) + set_word(i, get_word(i) | rt.get_word(i)); + return *this; + } + friend constexpr bitset operator|(bitset a, const bitset &b) noexcept + { + return a |= b; + } + constexpr bitset &operator^=(const bitset &rt) noexcept + { + for(std::size_t i = 0; i < word_count; i++) + set_word(i, get_word(i) ^ rt.get_word(i)); + return *this; + } + friend constexpr bitset operator^(bitset a, const bitset &b) noexcept + { + return a ^= b; + } + constexpr bitset &flip() noexcept + { + for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++) + set_word(i, ~get_word(i)); + set_word(get_word_index(bit_count - 1), + get_word(get_word_index(bit_count - 1)) + ^ static_cast( + static_cast(get_word_mask(bit_count - 1) << 1) - 1)); + return *this; + } + constexpr bitset operator~() const noexcept + { + bitset retval = *this; + retval.flip(); + return retval; + } + constexpr bitset &operator<<=(std::size_t shiftCount) noexcept + { + if(shiftCount >= bit_count) + return reset(); + std::size_t shiftWord_count = shiftCount / word_bit_count; + std::size_t shiftBitCount = shiftCount % word_bit_count; + if(shiftBitCount == 0) + { + for(std::size_t i = word_count; i > 0; i--) + { + std::size_t index = i - 1; + set_word(index, get_word_checked(index - shiftWord_count)); + } + } + else + { + for(std::size_t i = word_count; i > 0; i--) + { + std::size_t index = i - 1; + Word_type highWord = get_word_checked(index - shiftWord_count); + Word_type lowWord = get_word_checked(index - 1 - shiftWord_count); + set_word( + index, + (lowWord >> (word_bit_count - shiftBitCount)) | (highWord << shiftBitCount)); + } + } + if(word_count != 0) + set_word(word_count - 1, + get_word(word_count - 1) + & static_cast( + static_cast(get_word_mask(bit_count - 1) << 1) - 1)); + return *this; + } + constexpr bitset operator<<(std::size_t shiftCount) const noexcept + { + bitset retval = *this; + retval <<= shiftCount; + return retval; + } + constexpr bitset &operator>>=(std::size_t shiftCount) noexcept + { + if(shiftCount >= bit_count) + return reset(); + std::size_t shiftWord_count = shiftCount / word_bit_count; + std::size_t shiftBitCount = shiftCount % word_bit_count; + if(shiftBitCount == 0) + { + for(std::size_t index = 0; index < word_count; index++) + { + set_word(index, get_word_checked(index + shiftWord_count)); + } + } + else + { + for(std::size_t index = 0; index < word_count; index++) + { + Word_type highWord = get_word_checked(index + 1 + shiftWord_count); + Word_type lowWord = get_word_checked(index + shiftWord_count); + set_word( + index, + (lowWord >> shiftBitCount) | (highWord << (word_bit_count - shiftBitCount))); + } + } + return *this; + } + constexpr bitset operator>>(std::size_t shiftCount) const noexcept + { + bitset retval = *this; + retval >>= shiftCount; + return retval; + } + constexpr bitset &set() noexcept + { + if(word_count == 0) + return *this; + for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++) + set_word(i, static_cast(-1)); + set_word( + get_word_index(bit_count - 1), + static_cast(static_cast(get_word_mask(bit_count - 1) << 1) - 1)); + return *this; + } + constexpr bitset &set(std::size_t bit_index, bool value = true) + { + if(bit_index >= bit_count) + throw std::out_of_range("bit_index out of range in bitset::set"); + operator[](bit_index) = value; + return *this; + } + constexpr bitset &reset() noexcept + { + for(std::size_t i = 0; i < word_count; i++) + set_word(i, 0); + return *this; + } + constexpr bitset &reset(std::size_t bit_index) + { + if(bit_index >= bit_count) + throw std::out_of_range("bit_index out of range in bitset::reset"); + operator[](bit_index) = false; + return *this; + } + constexpr bitset &flip(std::size_t bit_index) + { + if(bit_index >= bit_count) + throw std::out_of_range("bit_index out of range in bitset::flip"); + operator[](bit_index).flip(); + return *this; + } + constexpr unsigned long long to_ullong() const + { + unsigned long long retval = 0; + constexpr std::size_t ullBitCount = std::numeric_limits::digits; + for(std::size_t i = 0; i < word_count; i++) + { + if(i * word_bit_count >= ullBitCount) + { + if(get_word(i) != 0) + throw std::overflow_error("bit set value too large in bitset::to_ullong"); + } + else + { + auto word = get_word(i); + auto shiftedWord = static_cast(word) << i * word_bit_count; + if((shiftedWord >> i * word_bit_count) != word) + throw std::overflow_error("bit set value too large in bitset::to_ullong"); + retval |= shiftedWord; + } + } + return retval; + } + constexpr unsigned long to_ulong() const + { + unsigned long long retval = to_ullong(); + if(retval > std::numeric_limits::max()) + throw std::overflow_error("bit set value too large in bitset::to_ulong"); + return retval; + } + static constexpr std::size_t npos = -1; // not in std::bitset + constexpr std::size_t find_first(bool value, std::size_t start = 0) const + noexcept // not in std::bitset + { + if(start >= bit_count) + return npos; + constexpr std::size_t endWordIndex = get_word_index(bit_count - 1); + std::size_t startWordIndex = get_word_index(start); + auto startWord = get_word(startWordIndex); + if(!value) + { + if(startWordIndex == endWordIndex) + startWord ^= static_cast( + static_cast(get_word_mask(bit_count - 1) << 1) - 1); + else + startWord = ~startWord; + } + auto mask = get_word_mask(start); + for(std::size_t retval = start; mask != 0; mask <<= 1, retval++) + { + if(startWord & mask) + return retval; + } + if(startWordIndex == endWordIndex) + return npos; + for(std::size_t word_index = startWordIndex + 1; word_index < endWordIndex; word_index++) + { + auto word = get_word(word_index); + if(word == static_cast(value ? 0 : -1)) + continue; + if(!value) + word = ~word; + mask = 1; + std::size_t retval = word_index * word_bit_count; + for(; mask != 0; mask <<= 1, retval++) + { + if(word & mask) + break; + } + return retval; + } + auto endWord = get_word(endWordIndex); + if(!value) + endWord ^= static_cast( + static_cast(get_word_mask(bit_count - 1) << 1) - 1); + if(endWord == 0) + return npos; + mask = 1; + std::size_t retval = endWordIndex * word_bit_count; + for(; mask != 0; mask <<= 1, retval++) + { + if(endWord & mask) + break; + } + return retval; + } + constexpr std::size_t find_last(bool value, std::size_t start = npos) const + noexcept // not in std::bitset + { + if(bit_count == 0) + return npos; + if(start >= bit_count) + start = bit_count - 1; + std::size_t startWordIndex = get_word_index(start); + auto startWord = get_word(startWordIndex); + if(!value) + { + if(startWordIndex == get_word_index(bit_count - 1)) + startWord ^= static_cast( + static_cast(get_word_mask(bit_count - 1) << 1) - 1); + else + startWord = ~startWord; + } + auto mask = get_word_mask(start); + for(std::size_t retval = start; mask != 0; mask >>= 1, retval--) + { + if(startWord & mask) + return retval; + } + for(std::size_t word_index = startWordIndex - 1, i = 0; i < startWordIndex; + word_index--, i++) + { + auto word = get_word(word_index); + if(word == static_cast(value ? 0 : -1)) + continue; + if(!value) + word = ~word; + mask = get_word_mask(word_bit_count - 1); + std::size_t retval = word_index * word_bit_count + (word_bit_count - 1); + for(; mask != 0; mask >>= 1, retval--) + { + if(word & mask) + break; + } + return retval; + } + return npos; + } + constexpr std::size_t hash() const noexcept // not in std::bitset + { + std::size_t retval = 0; + for(std::size_t i = 0; i < word_count; i++) + { + retval ^= get_word(i); + } + return retval; + } +}; + +template +constexpr std::size_t bitset::npos; +} +} + +namespace std +{ +template +struct hash> +{ + constexpr std::size_t operator()(const vulkan_cpu::util::bitset &v) const noexcept + { + return v.hash(); + } +}; +} + +#endif /* UTIL_BITSET_H_ */