Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion config/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# requirement is inherited by the test subprojects too, and a relative
# reference would be resolved from each of their directories.

import testing ;

project /boost/openmethod/config ;

obj has_reflection : has_reflection.cpp : <cxxflags>-freflection ;
Expand All @@ -22,5 +24,11 @@ explicit has_reflection ;
# needs. Probing beats naming an architecture - a <architecture>x86 conditional
# does not match every toolset spelling, and a compiler that rejects -mbmi2
# outright would take the directory down with it.
obj has_bmi2 : has_bmi2.cpp : <cxxflags>-mbmi2 <include>../include ;
#
# `run`, not `obj`: the compiler accepts -mbmi2 - and emits `pext` - whenever
# the toolchain supports the extension, whether or not the machine running the
# build implements it in hardware. A handful of CI runners pair a BMI2-capable
# compiler with pre-Haswell (or feature-masked) silicon, where the instruction
# traps with SIGILL; a compile-only check cannot see that; see has_bmi2.cpp.
run has_bmi2.cpp : : : <cxxflags>-mbmi2 <include>../include ;
explicit has_bmi2 ;
20 changes: 18 additions & 2 deletions config/has_bmi2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,29 @@
// what the test suite needs to know is whether that header will let the policy
// be used, which is a slightly narrower question than whether some spelling of
// pext compiles.
//
// The probe runs, it does not just compile. GCC and clang accept -mbmi2, and
// therefore emit `pext`, whenever the *toolchain* supports the extension -
// that says nothing about whether the machine actually running the build
// implements it in hardware. A CI runner can advertise a BMI2-capable
// compiler while executing on pre-Haswell (or virtualized/feature-masked)
// silicon, where the instruction traps with SIGILL. ../Jamfile turns this
// into a `run` target for exactly that reason: UPDATE_NOW executes the
// program, and a trap fails the check the same way a compile error would.
//
// `argc` keeps the operands from being folded to a compile-time constant, so
// optimization cannot turn the one `pext` this program executes into a
// no-op.

#include <boost/openmethod/policies/minimal_cover_hash.hpp>

#include <cstdint>

static_assert(BOOST_OPENMETHOD_HAS_PEXT);

auto probe(std::uint64_t value, std::uint64_t mask) -> std::uint64_t {
return boost::openmethod::detail::pext64(value, mask);
auto main(int argc, char**) -> int {
auto value = std::uint64_t(argc) | (std::uint64_t(1) << 63);
auto mask = std::uint64_t(argc) * 0x5555555555555555ull;

return boost::openmethod::detail::pext64(value, mask) == 0xdeadbeef ? 1 : 0;
}
40 changes: 28 additions & 12 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,28 @@ set(BOOST_OPENMETHOD_TEST_PCH_HEADERS

set(boost_openmethod_pch_owner "")

# minimal_cover_hash dispatches with BMI2's pext. A compiler accepts the
# enabling flag - and therefore emits `pext` - whenever the toolchain supports
# the extension, whatever the machine actually running the build implements in
# hardware; on pre-Haswell (or feature-masked) x86-64 the instruction traps
# with SIGILL. check_cxx_source_runs actually executes the probe, once, for
# exactly that reason - a plain try_compile cannot see the difference. See
# config/has_bmi2.cpp, and config/Jamfile for the b2 equivalent and the CI
# failure that motivated both.
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "MSVC")
set(BOOST_OPENMETHOD_BMI2_FLAG /arch:AVX2)
else()
set(BOOST_OPENMETHOD_BMI2_FLAG -mbmi2)
endif()

include(CheckCXXSourceRuns)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/../config/has_bmi2.cpp" BOOST_OPENMETHOD_HAS_BMI2_SOURCE)
set(CMAKE_REQUIRED_FLAGS ${BOOST_OPENMETHOD_BMI2_FLAG})
set(CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/../include")
check_cxx_source_runs("${BOOST_OPENMETHOD_HAS_BMI2_SOURCE}" BOOST_OPENMETHOD_HAS_BMI2)
unset(CMAKE_REQUIRED_FLAGS)
unset(CMAKE_REQUIRED_INCLUDES)

file(GLOB test_cpp_files "test_*.cpp")

foreach(test_cpp ${test_cpp_files})
Expand All @@ -107,21 +129,15 @@ foreach(test_cpp ${test_cpp_files})

# minimal_cover_hash dispatches with BMI2's pext, and because `hash` is
# inlined into every call the instruction has to be enabled for the whole
# translation unit. Only the tests that name the policy need it, and only on
# x86-64 - the 64-bit pext intrinsic does not exist in 32-bit mode:
# elsewhere BOOST_OPENMETHOD_HAS_PEXT is 0 and the policy drops out of both.
# translation unit. Only the tests that name the policy need it, and only
# where BOOST_OPENMETHOD_HAS_BMI2 (computed above) found the instruction
# both compiles *and runs* - elsewhere BOOST_OPENMETHOD_HAS_PEXT is 0 and
# the policy drops out of both.
set(test_needs_bmi2 FALSE)

if (test MATCHES "minimal_cover_hash|hash_policies" AND
CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
if (test MATCHES "minimal_cover_hash|hash_policies" AND BOOST_OPENMETHOD_HAS_BMI2)
set(test_needs_bmi2 TRUE)

if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR
CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "MSVC")
target_compile_options(${test_target} PRIVATE /arch:AVX2)
else()
target_compile_options(${test_target} PRIVATE -mbmi2)
endif()
target_compile_options(${test_target} PRIVATE ${BOOST_OPENMETHOD_BMI2_FLAG})
endif()

file(READ ${test_cpp} test_cpp_contents)
Expand Down
1 change: 1 addition & 0 deletions test/test_hash_policies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ auto base_of(std::size_t module, std::size_t spread) -> std::uintptr_t {
auto ids_over(std::size_t n, std::size_t modules, std::size_t spread)
-> std::vector<bom::type_id> {
std::vector<std::uintptr_t> at;
at.reserve(modules);

for (std::size_t module = 0; module != modules; ++module) {
at.push_back(base_of(module, spread));
Expand Down
Loading