benchmark/test/AssemblyTests.cmake
Alexander Popov 8545dfb3ea
Fix DoNotOptimize() GCC copy overhead (#1340) (#1410)
* Fix DoNotOptimize() GCC copy overhead (#1340)

The issue is that GCC DoNotOptimize() does a full copy of an argument
if it's not a pointer and it slows down a benchmark. If an argument is big
enough there is a memcpy() call for copying the argument. An argument
object can be a big object so DoNotOptimize() could add sufficient
overhead and affects benchmark results.

The cause is in GCC behavior with asm volatile constraints. Looks like GCC
trying to use r(register) constraint for all cases despite object size.
See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105519

The solution is the split DoNotOptimize() in two cases - value fits
in register and value doesn't fit in register. And use case specific
asm constraint. std::is_trivially_copyable trait is needed because
"+r" constraint doesn't work with non trivial copyable objects.

- Fix requires support C++11 feature std::is_trivially_copyable from GCC
  compiler. The feature has been supported since GCC 5
- Fallback for GCC version < 5 still exists but it uses "m" constraint
  which means a little bit more overhead in some cases
- Add assembly tests for issued cases

Fixes #1340

* Add supported compiler versions info for assembly tests

- Assembly tests are inherently non-portable. So explicitly add GCC
  and Clang versions required for reliable tests passed
- Write a warning message if the current compiler version isn't supported
2022-06-20 10:12:58 +01:00

68 lines
2.3 KiB
CMake

set(CLANG_SUPPORTED_VERSION "5.0.0")
set(GCC_SUPPORTED_VERSION "5.5.0")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL ${CLANG_SUPPORTED_VERSION})
message (WARNING
"Unsupported Clang version " ${CMAKE_CXX_COMPILER_VERSION}
". Expected is " ${CLANG_SUPPORTED_VERSION}
". Assembly tests may be broken.")
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL ${GCC_SUPPORTED_VERSION})
message (WARNING
"Unsupported GCC version " ${CMAKE_CXX_COMPILER_VERSION}
". Expected is " ${GCC_SUPPORTED_VERSION}
". Assembly tests may be broken.")
endif()
else()
message (WARNING "Unsupported compiler. Assembly tests may be broken.")
endif()
include(split_list)
set(ASM_TEST_FLAGS "")
check_cxx_compiler_flag(-O3 BENCHMARK_HAS_O3_FLAG)
if (BENCHMARK_HAS_O3_FLAG)
list(APPEND ASM_TEST_FLAGS -O3)
endif()
check_cxx_compiler_flag(-g0 BENCHMARK_HAS_G0_FLAG)
if (BENCHMARK_HAS_G0_FLAG)
list(APPEND ASM_TEST_FLAGS -g0)
endif()
check_cxx_compiler_flag(-fno-stack-protector BENCHMARK_HAS_FNO_STACK_PROTECTOR_FLAG)
if (BENCHMARK_HAS_FNO_STACK_PROTECTOR_FLAG)
list(APPEND ASM_TEST_FLAGS -fno-stack-protector)
endif()
split_list(ASM_TEST_FLAGS)
string(TOUPPER "${CMAKE_CXX_COMPILER_ID}" ASM_TEST_COMPILER)
macro(add_filecheck_test name)
cmake_parse_arguments(ARG "" "" "CHECK_PREFIXES" ${ARGV})
add_library(${name} OBJECT ${name}.cc)
target_link_libraries(${name} PRIVATE benchmark::benchmark)
set_target_properties(${name} PROPERTIES COMPILE_FLAGS "-S ${ASM_TEST_FLAGS}")
set(ASM_OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${name}.s")
add_custom_target(copy_${name} ALL
COMMAND ${PROJECT_SOURCE_DIR}/tools/strip_asm.py
$<TARGET_OBJECTS:${name}>
${ASM_OUTPUT_FILE}
BYPRODUCTS ${ASM_OUTPUT_FILE})
add_dependencies(copy_${name} ${name})
if (NOT ARG_CHECK_PREFIXES)
set(ARG_CHECK_PREFIXES "CHECK")
endif()
foreach(prefix ${ARG_CHECK_PREFIXES})
add_test(NAME run_${name}_${prefix}
COMMAND
${LLVM_FILECHECK_EXE} ${name}.cc
--input-file=${ASM_OUTPUT_FILE}
--check-prefixes=CHECK,CHECK-${ASM_TEST_COMPILER}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endforeach()
endmacro()