diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..64d2f48d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Vscode does not like to build outside of the source tree +# (multiple glitches) + +.vscode +test/.vscode diff --git a/compiler_bugs/msvc/sfinae.cpp b/compiler_bugs/msvc/sfinae.cpp new file mode 100644 index 00000000..41fa3d63 --- /dev/null +++ b/compiler_bugs/msvc/sfinae.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +template +constexpr auto Constructible_v = std::is_constructible_v; + +template +struct ATemplate { + alignas(alignof(Q)) char space_[sizeof(Q)]; + + template + constexpr static auto FitsInSpace_v = sizeof(T) <= sizeof(Q); + + template + std::enable_if_t< + FitsInSpace_v + && + #ifndef TRIGGER_MSVC_SFINAE_BUG + bool( + #endif + Constructible_v + #ifndef TRIGGER_MSVC_SFINAE_BUG + ) + #endif + , + T * + > + sfinaeFunction(Args &&...args) { + T *rv = new(static_cast(space_)) T(std::forward(args)...); + return rv; + } + +}; + +auto triggerError(ATemplate &m) { + return m.sfinaeFunction(nullptr); +} + +int main(int, const char *[]) { return 0; } diff --git a/inc/zoo/AlignedStorage.h b/inc/zoo/AlignedStorage.h index 4689aeca..ee1a0250 100644 --- a/inc/zoo/AlignedStorage.h +++ b/inc/zoo/AlignedStorage.h @@ -122,13 +122,21 @@ struct AlignedStorage { template #define PP_ZOO_BUILD_EXPRESSION \ - impl::build(*as(), std::forward(args)...) - auto build(Args &&...args) noexcept(noexcept(PP_ZOO_BUILD_EXPRESSION)) -> + impl::build(*this->as(), std::forward(args)...) std::enable_if_t< SuitableType() && - impl::Constructible_v, + #ifdef _MSC_VER + bool( + #endif + impl::Constructible_v + #ifdef _MSC_VER + ) + #endif + , + T * > + build(Args &&...args) noexcept(noexcept(PP_ZOO_BUILD_EXPRESSION)) { PP_ZOO_BUILD_EXPRESSION; #undef PP_ZOO_BUILD_EXPRESSION diff --git a/inc/zoo/algorithm/cfs.h b/inc/zoo/algorithm/cfs.h index 505250ea..87ae04aa 100644 --- a/inc/zoo/algorithm/cfs.h +++ b/inc/zoo/algorithm/cfs.h @@ -1,29 +1,27 @@ #ifndef ZOO_CFS_CACHE_FRIENDLY_SEARCH #define ZOO_CFS_CACHE_FRIENDLY_SEARCH -#include +#include "zoo/algorithm/less.h" +#include "zoo/meta/log.h" #ifndef SIMPLIFY_INCLUDES // because of std::declval needed to default comparator #include // because of std::decay needed to decay deferenced iterator #include -#endif -namespace zoo { +#ifdef _MSC_VER +#include +#endif -constexpr unsigned long long log2Floor(unsigned long long arg) { - return 63 - __builtin_clzll(arg); -} +#endif -constexpr unsigned long long log2Ceiling(unsigned long long arg) { - return 63 - __builtin_clzll(2*arg - 1); -} +namespace zoo { template void transformToCFS(Output output, Input base, Input end) { auto s = end - base; - auto logP = log2Floor(s + 1); // n + auto logP = meta::logFloor(s + 1); // n auto power2 = 1ul << logP; auto fullSubtreeSize = power2 - 1; // Full tree has (2^n) - 1 elements @@ -52,7 +50,8 @@ void transformToCFS(Output output, Input base, Input end) { } // now just write the excess leaves - for(auto ndx = 0ul, top = 2*excess; ndx < top; ndx += 2) { + auto top = 2*excess; + for(auto ndx = 0ll; ndx < top; ndx += 2) { *output++ = *(base + ndx); } } @@ -167,7 +166,7 @@ struct ValidResult { template auto validHeap( - I base, int current, int max, Comparator c = Comparator{} + I base, long current, long max, Comparator c = Comparator{} ) -> ValidResult { for(;;) { auto higherSubtree = current*2 + 2; diff --git a/inc/zoo/algorithm/quicksort.h b/inc/zoo/algorithm/quicksort.h index a53669c9..980174f4 100644 --- a/inc/zoo/algorithm/quicksort.h +++ b/inc/zoo/algorithm/quicksort.h @@ -8,6 +8,10 @@ #include // for temporary storage #include +#ifdef _MSC_VER +#include +#endif + namespace zoo { template diff --git a/inc/zoo/meta/log.h b/inc/zoo/meta/log.h index d02a555c..cae6d2f1 100644 --- a/inc/zoo/meta/log.h +++ b/inc/zoo/meta/log.h @@ -5,28 +5,39 @@ namespace zoo { namespace meta { -constexpr int logFloor(uint64_t arg) { - return 63 - __builtin_clzll(arg); -} - -constexpr int logCeiling(uint64_t arg) { - auto floorLog = logFloor(arg); - return floorLog + ((arg ^ (1ull << floorLog)) ? 1 : 0); -} - /// The algorithm is, from the perspective of the most significant bit set, to copy it /// downward to all positions. /// First copy it once, meaning a group of two copies of the two most significant bit /// Then copy it again, making a group of four copies, then 8 copies... template -constexpr int logCeiling_WithoutIntrinsic(T value) { +constexpr int logFloor_WithoutIntrinsic(T value) { constexpr auto NBitsTotal = sizeof(T) * 8; for(auto groupSize = 1; groupSize < NBitsTotal; groupSize <<= 1) { value |= (value >> groupSize); } return PopcountLogic, T>::execute(value) - 1; } - + +#ifdef _MSC_VER +// change to use the relevant functions in C++ 20's header +// when bumping to C++ 20 +constexpr int logFloor(uint64_t arg) { + return logFloor_WithoutIntrinsic(arg); +} +#else +constexpr int logFloor(uint64_t arg) { + return 63 - __builtin_clzll(arg); +} +#endif + +constexpr int logCeiling(uint64_t arg) { + auto floorLog = logFloor(arg); + return + floorLog + + // turn off the most significant bit and convert to 1 or 0 + ((arg ^ (1ull << floorLog)) ? 1 : 0); +} + }} #endif diff --git a/inc/zoo/meta/popcount.h b/inc/zoo/meta/popcount.h index 5b57b364..3b00056b 100644 --- a/inc/zoo/meta/popcount.h +++ b/inc/zoo/meta/popcount.h @@ -69,6 +69,10 @@ constexpr T PopcountLogic::execute(T input) { Recursion::execute((input >> HalvedGroupSize) & CombiningMask); } +#ifdef _MSC_VER +template +using PopcountIntrinsic = PopcountLogic; +#else template struct PopcountIntrinsic { constexpr static T execute(T input) { @@ -84,6 +88,7 @@ struct PopcountIntrinsic { return rv; } }; +#endif }} diff --git a/inc/zoo/swar/SWAR.h b/inc/zoo/swar/SWAR.h index f982adba..d2bdd6e8 100644 --- a/inc/zoo/swar/SWAR.h +++ b/inc/zoo/swar/SWAR.h @@ -6,6 +6,10 @@ #include +#ifdef _MSC_VER +#include +#endif + namespace zoo { namespace swar { using u64 = uint64_t; @@ -27,13 +31,14 @@ constexpr uint64_t popcount(uint64_t a) noexcept { /// Index into the bits of the type T that contains the MSB. template constexpr std::make_unsigned_t msbIndex(T v) noexcept { - return 8*sizeof(T) - 1 - __builtin_clzll(v); + return meta::logFloor(v); } /// Index into the bits of the type T that contains the LSB. template constexpr std::make_unsigned_t lsbIndex(T v) noexcept { - return __builtin_ctzll(v) + 1; + // ~v & (v - 1) turns on all trailing zeroes, zeroes the rest + return meta::logFloor(1 + (~v & (v - 1))); } /// Core abstraction around SIMD Within A Register (SWAR). Specifies 'lanes' @@ -44,12 +49,14 @@ constexpr std::make_unsigned_t lsbIndex(T v) noexcept { template struct SWAR { using type = T; - constexpr static inline auto NBits = NBits_; - constexpr static inline auto Lanes = sizeof(T) * 8 / NBits; - constexpr static inline auto NSlots = Lanes; - constexpr static T BitMod = sizeof(T)*8 % NBits; - constexpr static T ValidBitsCount = sizeof(T)*8 - BitMod; - constexpr static T AllOnes = (BitMod == 0) ? ~(T(0)) : ((T(1) << ValidBitsCount) -1); + constexpr static inline std::make_unsigned_t + NBits = NBits_, + BitWidth = sizeof(T) * 8, + Lanes = BitWidth / NBits, + NSlots = Lanes, + PaddingBitsCount = BitWidth % NBits, + SignificantBitsCount = BitWidth - PaddingBitsCount, + AllOnes = ~std::make_unsigned_t{0} >> PaddingBitsCount; SWAR() = default; constexpr explicit SWAR(T v): m_v(v) {} @@ -92,13 +99,21 @@ struct SWAR { /// The SWAR lane index that contains the MSB. It is not the bit index of the MSB. /// IE: 4 bit wide 32 bit SWAR: 0x0040'0000 will return 5, not 22 (0 indexed). - constexpr int top() const noexcept { return msbIndex(m_v) / NBits; } - constexpr int lsbIndex() const noexcept { return __builtin_ctzll(m_v) / NBits; } + constexpr auto top() const noexcept { return msbIndex(m_v) / NBits; } + constexpr auto lsbIndex() const noexcept { return swar::lsbIndex(m_v) / NBits; } constexpr SWAR setBit(int index, int bit) const noexcept { return SWAR(m_v | (T(1) << (index * NBits + bit))); } + constexpr SWAR shiftLanesLeft(int laneCount) const noexcept { + return SWAR(value() << (NBits * laneCount)); + } + + constexpr SWAR shiftLanesRight(int laneCount) const noexcept { + return SWAR(value() >> (NBits * laneCount)); + } + constexpr auto blitElement(int index, T value) const noexcept { auto elementMask = ((T(1) << NBits) - 1) << (index * NBits); return SWAR((m_v & ~elementMask) | (value << (index * NBits))); @@ -110,14 +125,6 @@ struct SWAR { return (*this & ~IsolationMask) | (other & IsolationMask); } - constexpr SWAR shiftLanesLeft(int laneCount) const noexcept { - return SWAR(value() << (NBits * laneCount)); - } - - constexpr SWAR shiftLanesRight(int laneCount) const noexcept { - return SWAR(value() >> (NBits * laneCount)); - } - T m_v; }; diff --git a/inc/zoo/traits/is_container.h b/inc/zoo/traits/is_container.h index 39a824ad..254f55fb 100644 --- a/inc/zoo/traits/is_container.h +++ b/inc/zoo/traits/is_container.h @@ -3,6 +3,11 @@ #ifndef SIMPLIFY_INCLUDES #include + +#ifdef _MSC_VER +#include +#endif + #endif namespace zoo { diff --git a/inc/zoo/util/range_equivalence.h b/inc/zoo/util/range_equivalence.h index 97fc14c5..7517b825 100644 --- a/inc/zoo/util/range_equivalence.h +++ b/inc/zoo/util/range_equivalence.h @@ -8,8 +8,8 @@ namespace zoo { template auto operator==(const C1 &l, const C2 &r) -> std::enable_if_t< - zoo::is_container_v and - zoo::is_container_v, + bool(zoo::is_container_v) and + bool(zoo::is_container_v), bool > { diff --git a/test/AlignedStorage.cpp b/test/AlignedStorage.cpp index 11667f82..765f1b62 100644 --- a/test/AlignedStorage.cpp +++ b/test/AlignedStorage.cpp @@ -35,14 +35,14 @@ static_assert(alignof(A1::space_) == 1, "specific alignment not honored"); template std::false_type MayCallBuild(...); template -auto MayCallBuild(int, As &&...as) -> +auto MayCallBuild(As &&...as) -> decltype( std::declval().template build(std::forward(as)...), std::true_type{} ); template constexpr auto MayCallBuild_(As &&...as) { - return decltype(MayCallBuild(0, std::forward(as)...))::value; + return decltype(MayCallBuild(std::forward(as)...))::value; } static_assert( @@ -160,6 +160,7 @@ struct Typical { Typical() = default; Typical(const Typical &) = default; Typical(Typical &&) = default; + Typical(long s): state_(s) {} long state_; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 244de302..db03bb4b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,12 +1,6 @@ cmake_minimum_required (VERSION 3.8) set(CMAKE_BUILD_TYPE Debug) -set(CMAKE_CXX_STANDARD 17) - -set(CMAKE_CXX_FLAGS_UBSAN "-fsanitize=undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls -O1 -g") -# set(CMAKE_EXE_LINKER_FLAGS_UBSAN -fsanitize=undefined) -# set(CMAKE_SHARED_LINKER_FLAGS_UBSAN -fsanitize=undefined) -set(CMAKE_CXX_FLAGS_ASAN "-fsanitize=address -fno-omit-frame-pointer") project(ZooTest VERSION 1.0 LANGUAGES CXX) @@ -16,80 +10,146 @@ configure_file ( "${PROJECT_BINARY_DIR}/ZooTestConfig.h" ) -add_subdirectory(third_party EXCLUDE_FROM_ALL) -enable_testing() - -include_directories( - "${PROJECT_BINARY_DIR}" - ./inc - ../inc - ${TEST_THIRD_PARTY_INCLUDE_PATH} -) +if(MSVC) + # MSVC specific configuration + # Avoids multiple problems + + # Due to multiple bugs, forced upgrade to C++ 20 + set(CMAKE_CXX_STANDARD 20) + + # Set the policy to use the new behavior + if(POLICY CMP0067) + cmake_policy(SET CMP0067 NEW) + message(STATUS "Set policy") + endif() + + include_directories( + "${PROJECT_BINARY_DIR}" + ./inc + ../inc + ./third_party/Catch2/single_include + ) + + set( + ZOO_TEST_SOURCES + catch_main.cpp + any.cpp AlignedStorage.cpp AnyCallable.cpp AnyCallSignature.cpp + AnyExtended.cpp GenericPolicy.cpp FunctionPolicy.cpp + swar/BasicOperations.cpp + # map/BasicMap.cpp + # map/RobinHood.test.cpp + # map/RobinHood.hybrid.test.cpp + algorithm/cfs.cpp + algorithm/quicksort.cpp + egyptian.cpp var.cpp variant.cpp CopyMoveAbilities.cpp + ) + + # Simple executable for MSVC + add_executable(ZooTest ${ZOO_TEST_SOURCES}) + target_compile_definitions(ZooTest PUBLIC AVOID_MSVC_BUG_SFINAE_INVALID_EXPLICIT_TEMPLATE_ARGUMENT) + + try_compile( + MSVC_BUG_BUILD_RESULT + ${CMAKE_BINARY_DIR}/temporary + SOURCES + ${CMAKE_SOURCE_DIR}/../compiler_bugs/msvc/sfinae.cpp + CMAKE_FLAGS "-DCMAKE_CXX_STANDARD=17" + COMPILE_DEFINITIONS + -DTRIGGER_MSVC_SFINAE_BUG + OUTPUT_VARIABLE RESULT + ) + if(MSVC_BUG_BUILD_RESULT) + MESSAGE(FATAL_ERROR "Compilation of MSVC bug file succeeded, was the compiler bug fixed? ${RESULT}") + else() + MESSAGE(STATUS "File with MSVC bug build failed as expected ${RESULT}") + endif() + try_compile( + MSVC_OK_BUILD_RESULT + ${CMAKE_BINARY_DIR}/temporary + SOURCES + ${CMAKE_SOURCE_DIR}/../compiler_bugs/msvc/sfinae.cpp + CMAKE_FLAGS -DCMAKE_CXX_STANDARD=20 + OUTPUT_VARIABLE MSVC_OK_OUTPUT + ) + if(MSVC_OK_BUILD_RESULT) + MESSAGE(STATUS "CMAKE try_compile succeeded as expected: ${MSVC_OK_OUTPUT}") + else() + MESSAGE(FATAL_ERROR "CMAKE try_compile of non-problematic file did not succeed: ${MSVC_OK_OUTPUT}") + endif() + +else() + # Non-MSVC specific configuration (original content) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_FLAGS_UBSAN "-fsanitize=undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls -O1 -g") + set(CMAKE_CXX_FLAGS_ASAN "-fsanitize=address -fno-omit-frame-pointer") + + add_subdirectory(third_party EXCLUDE_FROM_ALL) + enable_testing() + + include_directories( + "${PROJECT_BINARY_DIR}" + ./inc + ../inc + ${TEST_THIRD_PARTY_INCLUDE_PATH} + ) + + if("UBSAN" STREQUAL "${CMAKE_BUILD_TYPE}") + set(ADDITIONAL_SOURCES "ubsan.cpp") + endif() + + message(STATUS "Additional sources:" ${ADDITIONAL_SOURCES}) + + set(CURRENT_EXECUTABLE "zooTest${CMAKE_BUILD_TYPE}") + + set(CATCH2_MAIN_SOURCE catch_main.cpp) + set( + TYPE_ERASURE_SOURCES + any.cpp AlignedStorage.cpp AnyCallable.cpp AnyCallSignature.cpp + AnyExtended.cpp GenericPolicy.cpp FunctionPolicy.cpp + ) + set( + SWAR_SOURCES + swar/BasicOperations.cpp + ) + set( + MAP_SOURCES + map/BasicMap.cpp map/RobinHood.test.cpp map/RobinHood.hybrid.test.cpp + ) + set(ALGORITHM_SOURCES algorithm/cfs.cpp algorithm/quicksort.cpp) + set(MISCELLANEA_SOURCES egyptian.cpp var.cpp variant.cpp CopyMoveAbilities.cpp) + set( + ZOO_TEST_SOURCES + ${CATCH2_MAIN_SOURCE} ${TYPE_ERASURE_SOURCES} ${ALGORITHM_SOURCES} + ${SWAR_SOURCES} + ${MISCELLANEA_SOURCES} + ) + + add_library(Catch2Main OBJECT ${CATCH2_MAIN_SOURCE}) + add_library(AlgorithmTest OBJECT ${ALGORITHM_SOURCES}) + add_library(TypeErasureTest OBJECT ${TYPE_ERASURE_SOURCES}) + add_library(SWARTest OBJECT ${SWAR_SOURCES}) + add_library(MapTest OBJECT ${MAP_SOURCES}) + add_library(Uncategorized OBJECT ${MISCELLANEA_SOURCES}) + + add_executable( + ${CURRENT_EXECUTABLE} ${ADDITIONAL_SOURCES} + ) + target_link_libraries(${CURRENT_EXECUTABLE} Catch2Main AlgorithmTest TypeErasureTest SWARTest Uncategorized) + + add_executable(algorithm2 $) + target_link_libraries(algorithm2 AlgorithmTest) + add_executable(type_erasure $) + target_link_libraries(type_erasure TypeErasureTest) + add_executable(swar $) + target_link_libraries(swar SWARTest) + add_executable(mapt $) + target_link_libraries(mapt MapTest) + + # CMake build: library tests + set(TEST_APP_NAME "${CURRENT_EXECUTABLE}Test") + add_executable(${TEST_APP_NAME} ${ZOO_TEST_SOURCES}) + include_directories(${TEST_THIRD_PARTY_INCLUDE_PATH}) + enable_testing() + ParseAndAddCatchTests(${TEST_APP_NAME}) -if("UBSAN" STREQUAL "${CMAKE_BUILD_TYPE}") - set(ADDITIONAL_SOURCES "ubsan.cpp") endif() - -message(STATUS "Additional sources:" ${ADDITIONAL_SOURCES}) - -set(CURRENT_EXECUTABLE "zooTest${CMAKE_BUILD_TYPE}") - -set(CATCH2_MAIN_SOURCE catch_main.cpp) -set( - TYPE_ERASURE_SOURCES - any.cpp AlignedStorage.cpp AnyCallable.cpp AnyCallSignature.cpp - AnyExtended.cpp GenericPolicy.cpp FunctionPolicy.cpp -) -set( - SWAR_SOURCES - swar/BasicOperations.cpp -) -set( - MAP_SOURCES - map/BasicMap.cpp map/RobinHood.test.cpp map/RobinHood.hybrid.test.cpp -) -set(ALGORITHM_SOURCES algorithm/cfs.cpp algorithm/quicksort.cpp) -set(MISCELLANEA_SOURCES egyptian.cpp var.cpp variant.cpp CopyMoveAbilities.cpp) -set( - ZOO_TEST_SOURCES - ${CATCH2_MAIN_SOURCE} ${TYPE_ERASURE_SOURCES} ${ALGORITHM_SOURCES} - ${SWAR_SOURCES} - ${MISCELLANEA_SOURCES} -) - -add_library(Catch2Main OBJECT ${CATCH2_MAIN_SOURCE}) -add_library(AlgorithmTest OBJECT ${ALGORITHM_SOURCES}) -add_library(TypeErasureTest OBJECT ${TYPE_ERASURE_SOURCES}) -add_library(SWARTest OBJECT ${SWAR_SOURCES}) -add_library(MapTest OBJECT ${MAP_SOURCES}) -add_library(Uncategorized OBJECT ${MISCELLANEA_SOURCES}) - -add_executable( - ${CURRENT_EXECUTABLE} ${ADDITIONAL_SOURCES} -) -target_link_libraries(${CURRENT_EXECUTABLE} Catch2Main AlgorithmTest TypeErasureTest SWARTest Uncategorized) - -add_executable(algorithm2 $) -target_link_libraries(algorithm2 AlgorithmTest) -add_executable(type_erasure $) -target_link_libraries(type_erasure TypeErasureTest) -add_executable(swar $) -target_link_libraries(swar SWARTest) -add_executable(mapt $) -target_link_libraries(mapt MapTest) - -# CMake build : library tests - -#configure variables -set(TEST_APP_NAME "${CURRENT_EXECUTABLE}Test") - -add_executable(${TEST_APP_NAME} ${ZOO_TEST_SOURCES}) - -#set includes -include_directories(${TEST_THIRD_PARTY_INCLUDE_PATH}) - -# Turn on CMake testing capabilities -enable_testing() - -#parse catch tests -ParseAndAddCatchTests(${TEST_APP_NAME}) diff --git a/test/FunctionPolicy.cpp b/test/FunctionPolicy.cpp index 7438cdf1..74fbd67c 100644 --- a/test/FunctionPolicy.cpp +++ b/test/FunctionPolicy.cpp @@ -173,6 +173,7 @@ TEST_CASE( F fun; REQUIRE(!fun); auto anythingTriviallyDestructible = [](){}; + static_assert(std::is_trivially_destructible_v); fun = anythingTriviallyDestructible; CHECK(fun); REQUIRE(!fun.isDefault()); diff --git a/test/inc/zoo/var.h b/test/inc/zoo/var.h index f817031c..3a4bd8cf 100644 --- a/test/inc/zoo/var.h +++ b/test/inc/zoo/var.h @@ -171,7 +171,7 @@ struct Var: visit( [&](auto &&m) { using Source = meta::remove_cr_t; - meta::move_in_place(as(), std::move(m)); + meta::move_in_place(this->as(), std::move(m)); }, v ); diff --git a/test/inc/zoo/variant.h b/test/inc/zoo/variant.h index ed30f9cd..893f73ab 100644 --- a/test/inc/zoo/variant.h +++ b/test/inc/zoo/variant.h @@ -90,7 +90,7 @@ struct Variant { visit( [&](const auto &c) { using Source = meta::remove_cr_t; - meta::copy_in_place(as(), c); + meta::copy_in_place(this->as(), c); }, v ); @@ -100,7 +100,7 @@ struct Variant { visit( [&](auto &&m) { using Source = meta::remove_cr_t; - meta::move_in_place(as(), std::move(m)); + meta::move_in_place(this->as(), std::move(m)); }, v ); diff --git a/test/swar/BasicOperations.cpp b/test/swar/BasicOperations.cpp index 9c6e8d42..b4480e54 100644 --- a/test/swar/BasicOperations.cpp +++ b/test/swar/BasicOperations.cpp @@ -159,12 +159,12 @@ static_assert(0x0E0E'0E0E == u32(broadcast<8>(SWAR<8, u32>(0x0000'000E)))); static_assert(0x6B6B'6B6B == u32(broadcast<8>(SWAR<8, u32>(0x0000'006B)))); static_assert(0x0808'0808'0808'0808ull == u64(broadcast<8>(SWAR<8, u64>(0x0000'0000'0000'0008ull)))); -static_assert(2 == lsbIndex(1<<1)); -static_assert(4 == lsbIndex(1<<3)); -static_assert(6 == lsbIndex(1<<5)); -static_assert(9 == lsbIndex(1<<8)); -static_assert(18 == lsbIndex(1<<17)); -static_assert(31 == lsbIndex(1<<30)); +static_assert(1 == lsbIndex(1<<1)); +static_assert(3 == lsbIndex(1<<3)); +static_assert(5 == lsbIndex(1<<5)); +static_assert(8 == lsbIndex(1<<8)); +static_assert(17 == lsbIndex(1<<17)); +static_assert(30 == lsbIndex(1<<30)); /* These tests were not catching errors known to have been present