Skip to content

Commit 4b06305

Browse files
authored
Merge fa2449a into 3924ee7
2 parents 3924ee7 + fa2449a commit 4b06305

File tree

9 files changed

+373
-13
lines changed

9 files changed

+373
-13
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*.dylib
77
*.cmake
88
!/cmake/*.cmake
9+
!/test/AssemblyTests.cmake
910
*~
1011
*.pyc
1112
__pycache__

.travis.yml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,23 +131,27 @@ matrix:
131131
- COMPILER=g++-7 C_COMPILER=gcc-7 BUILD_TYPE=Debug
132132

133133
before_script:
134-
- if [ -z "$BUILD_32_BITS" ]; then
135-
export BUILD_32_BITS=OFF && echo disabling 32 bit build;
136-
fi
137134
- if [ -n "${LIBCXX_BUILD}" ]; then
138135
source .travis-libcxx-setup.sh;
139136
fi
140137
- mkdir -p build && cd build
141138

142139
before_install:
140+
- if [ -z "$BUILD_32_BITS" ]; then
141+
export BUILD_32_BITS=OFF && echo disabling 32 bit build;
142+
fi
143143
- if [ -n "${INSTALL_GCC6_FROM_PPA}" ]; then
144144
sudo add-apt-repository -y "ppa:ubuntu-toolchain-r/test";
145145
sudo apt-get update --option Acquire::Retries=100 --option Acquire::http::Timeout="60";
146146
fi
147147

148148
install:
149149
- if [ -n "${INSTALL_GCC6_FROM_PPA}" ]; then
150-
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install g++-6;
150+
sudo -E apt-get -yq --no-install-suggests --no-install-recommends install g++-6;
151+
fi
152+
- if [ "${TRAVIS_OS_NAME}" == "linux" -a "${BUILD_32_BITS}" == "OFF" ]; then
153+
sudo -E apt-get -y --no-install-suggests --no-install-recommends install llvm-3.9-tools;
154+
sudo cp /usr/lib/llvm-3.9/bin/FileCheck /usr/local/bin/;
151155
fi
152156
- if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then
153157
PATH=~/.local/bin:${PATH};

CMakeLists.txt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,48 @@ option(BENCHMARK_DOWNLOAD_DEPENDENCIES "Allow the downloading and in-tree buildi
2727
# in cases where it is not possible to build or find a valid version of gtest.
2828
option(BENCHMARK_ENABLE_GTEST_TESTS "Enable building the unit tests which depend on gtest" ON)
2929

30+
set(ENABLE_ASSEMBLY_TESTS_DEFAULT OFF)
31+
function(should_enable_assembly_tests)
32+
if(CMAKE_BUILD_TYPE)
33+
string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LOWER)
34+
if (${CMAKE_BUILD_TYPE_LOWER} MATCHES "coverage")
35+
# FIXME: The --coverage flag needs to be removed when building assembly
36+
# tests for this to work.
37+
return()
38+
endif()
39+
endif()
40+
if (MSVC)
41+
return()
42+
elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
43+
return()
44+
elseif(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
45+
# FIXME: Make these work on 32 bit builds
46+
return()
47+
elseif(BENCHMARK_BUILD_32_BITS)
48+
# FIXME: Make these work on 32 bit builds
49+
return()
50+
endif()
51+
find_program(LLVM_FILECHECK_EXE FileCheck)
52+
if (LLVM_FILECHECK_EXE)
53+
set(LLVM_FILECHECK_EXE "${LLVM_FILECHECK_EXE}" CACHE PATH "llvm filecheck" FORCE)
54+
message(STATUS "LLVM FileCheck Found: ${LLVM_FILECHECK_EXE}")
55+
else()
56+
message(STATUS "Failed to find LLVM FileCheck")
57+
return()
58+
endif()
59+
set(ENABLE_ASSEMBLY_TESTS_DEFAULT ON PARENT_SCOPE)
60+
endfunction()
61+
should_enable_assembly_tests()
62+
63+
# This option disables the building and running of the assembly verification tests
64+
option(BENCHMARK_ENABLE_ASSEMBLY_TESTS "Enable building and running the assembly tests"
65+
${ENABLE_ASSEMBLY_TESTS_DEFAULT})
66+
3067
# Make sure we can import out CMake functions
3168
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
3269
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
3370

71+
3472
# Read the git tags to determine the project version
3573
include(GetGitVersion)
3674
get_git_version(GIT_VERSION)

include/benchmark/benchmark.h

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -302,16 +302,98 @@ BENCHMARK_UNUSED static int stream_init_anchor = InitializeStreams();
302302
// intended to add little to no overhead.
303303
// See: https://youtu.be/nXaxk27zwlk?t=2441
304304
#ifndef BENCHMARK_HAS_NO_INLINE_ASSEMBLY
305-
template <class Tp>
306-
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
307-
// Clang doesn't like the 'X' constraint on `value` and certain GCC versions
308-
// don't like the 'g' constraint. Attempt to placate them both.
309305
#if defined(__clang__)
310-
asm volatile("" : : "g"(value) : "memory");
306+
template <class Tp>
307+
struct CanUseRegisterOperandImp {
308+
#ifdef BENCHMARK_HAS_CXX11
309+
using DecayT = typename std::decay<Tp>::type;
310+
static const bool value =
311+
sizeof(DecayT) <= sizeof(long);
311312
#else
312-
asm volatile("" : : "i,r,m"(value) : "memory");
313+
static const bool value = false;
314+
#endif
315+
};
316+
template <class InputTp, bool = CanUseRegisterOperandImp<InputTp>::value>
317+
struct InlineAsmHelper {
318+
template <class Tp>
319+
static inline BENCHMARK_ALWAYS_INLINE
320+
void DoNotOptimize(Tp const& value) {
321+
asm volatile("" : : "r,m"(value) : "memory");
322+
}
323+
template <class Tp>
324+
static inline BENCHMARK_ALWAYS_INLINE
325+
void DoNotOptimize(Tp& value) {
326+
asm volatile("" : "=g"(value) : : "memory");
327+
}
328+
#ifdef BENCHMARK_HAS_CXX11
329+
template <class Tp>
330+
static inline BENCHMARK_ALWAYS_INLINE
331+
void DoNotOptimize(Tp&& value) {
332+
asm volatile("" : : "r"(value) : "memory");
333+
}
313334
#endif
335+
};
336+
template <class InputTp>
337+
struct InlineAsmHelper<InputTp, false> {
338+
template <class Tp>
339+
static inline BENCHMARK_ALWAYS_INLINE
340+
void DoNotOptimize(Tp const& value) {
341+
asm volatile("" : : "m"(value) : "memory");
342+
}
343+
template <class Tp>
344+
static inline BENCHMARK_ALWAYS_INLINE
345+
void DoNotOptimize(Tp& value) {
346+
asm volatile("" : "+m"(value) : : "memory");
347+
}
348+
#ifdef BENCHMARK_HAS_CXX11
349+
template <class Tp>
350+
static inline BENCHMARK_ALWAYS_INLINE
351+
void DoNotOptimize(Tp&& value) {
352+
asm volatile("" : : "r,m"(value) : "memory");
353+
}
354+
#endif
355+
};
356+
#else
357+
template <class InputTp>
358+
struct InlineAsmHelper {
359+
template <class Tp>
360+
static inline BENCHMARK_ALWAYS_INLINE
361+
void DoNotOptimize(Tp const& value) {
362+
asm volatile("" : : "r,m"(value) : "memory");
363+
}
364+
template <class Tp>
365+
static inline BENCHMARK_ALWAYS_INLINE
366+
void DoNotOptimize(Tp& value) {
367+
asm volatile("" : "+m,r"(value) : : "memory");
368+
}
369+
#ifdef BENCHMARK_HAS_CXX11
370+
template <class Tp>
371+
static inline BENCHMARK_ALWAYS_INLINE
372+
typename std::enable_if<!std::is_lvalue_reference<Tp>::value, void>::type
373+
DoNotOptimize(Tp&& value) {
374+
asm volatile("" : : "r,m"(value) : "memory");
375+
}
376+
#endif
377+
};
378+
#endif
379+
380+
template <class Tp>
381+
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
382+
InlineAsmHelper<Tp>::DoNotOptimize(value);
383+
}
384+
385+
#if defined(BENCHMARK_HAS_CXX11)
386+
template <class Tp>
387+
inline BENCHMARK_ALWAYS_INLINE
388+
typename std::enable_if<!std::is_lvalue_reference<Tp>::value, void>::type
389+
DoNotOptimize(Tp&& value) {
390+
static_assert(!std::is_lvalue_reference<Tp>::value, "");
391+
static_assert(!std::is_const<Tp>::value, "");
392+
InlineAsmHelper<Tp>::DoNotOptimize(static_cast<Tp&&>(value));
314393
}
394+
395+
template <class Tp> void DoNotOptimize(Tp const&& value) = delete;
396+
#endif
315397
// Force the compiler to flush pending writes to global memory. Acts as an
316398
// effective read/write barrier
317399
inline BENCHMARK_ALWAYS_INLINE void ClobberMemory() {

test/AssemblyTests.cmake

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
3+
set(ASM_TEST_FLAGS "")
4+
check_cxx_compiler_flag(-O3 BENCHMARK_HAS_O3_FLAG)
5+
if (BENCHMARK_HAS_O3_FLAG)
6+
list(APPEND ASM_TEST_FLAGS -O3)
7+
endif()
8+
9+
check_cxx_compiler_flag(-g0 BENCHMARK_HAS_G0_FLAG)
10+
if (BENCHMARK_HAS_G0_FLAG)
11+
list(APPEND ASM_TEST_FLAGS -g0)
12+
endif()
13+
14+
check_cxx_compiler_flag(-fno-stack-protector BENCHMARK_HAS_FNO_STACK_PROTECTOR_FLAG)
15+
if (BENCHMARK_HAS_FNO_STACK_PROTECTOR_FLAG)
16+
list(APPEND ASM_TEST_FLAGS -fno-stack-protector)
17+
endif()
18+
19+
split_list(ASM_TEST_FLAGS)
20+
string(TOUPPER "${CMAKE_CXX_COMPILER_ID}" ASM_TEST_COMPILER)
21+
22+
macro(add_filecheck_test name)
23+
cmake_parse_arguments(ARG "" "" "CHECK_PREFIXES" ${ARGV})
24+
add_library(${name} OBJECT ${name}.cc)
25+
set_target_properties(${name} PROPERTIES COMPILE_FLAGS "-S ${ASM_TEST_FLAGS}")
26+
set(ASM_OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${name}.s")
27+
add_custom_command(OUTPUT ${ASM_OUTPUT_FILE}
28+
COMMAND ${PROJECT_SOURCE_DIR}/tools/strip_asm.py
29+
$<TARGET_OBJECTS:${name}>
30+
${ASM_OUTPUT_FILE}
31+
DEPENDS ${name})
32+
add_custom_target(copy_${name} ALL
33+
SOURCES ${ASM_OUTPUT_FILE})
34+
if (NOT ARG_CHECK_PREFIXES)
35+
set(ARG_CHECK_PREFIXES "CHECK")
36+
endif()
37+
foreach(prefix ${ARG_CHECK_PREFIXES})
38+
add_test(NAME run_${name}_${prefix}
39+
COMMAND
40+
${LLVM_FILECHECK_EXE} ${name}.cc
41+
--input-file=${ASM_OUTPUT_FILE}
42+
--check-prefixes=CHECK,CHECK-${ASM_TEST_COMPILER}
43+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
44+
endforeach()
45+
endmacro()
46+

test/CMakeLists.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ if( NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG" )
2222
endforeach()
2323
endif()
2424

25+
check_cxx_compiler_flag(-O3 BENCHMARK_HAS_O3_FLAG)
26+
set(BENCHMARK_O3_FLAG "")
27+
if (BENCHMARK_HAS_O3_FLAG)
28+
set(BENCHMARK_O3_FLAG "-O3")
29+
endif()
30+
2531
# NOTE: These flags must be added after find_package(Threads REQUIRED) otherwise
2632
# they will break the configuration check.
2733
if (DEFINED BENCHMARK_CXX_LINKER_FLAGS)
@@ -156,6 +162,23 @@ if (BENCHMARK_ENABLE_GTEST_TESTS)
156162
add_gtest(statistics_test)
157163
endif(BENCHMARK_ENABLE_GTEST_TESTS)
158164

165+
###############################################################################
166+
# Assembly Unit Tests
167+
###############################################################################
168+
169+
if (BENCHMARK_ENABLE_ASSEMBLY_TESTS)
170+
if (NOT LLVM_FILECHECK_EXE)
171+
message(FATAL_ERROR "LLVM FileCheck is required when including this file")
172+
endif()
173+
include(AssemblyTests.cmake)
174+
add_filecheck_test(donotoptimize_assembly_test)
175+
endif()
176+
177+
178+
179+
###############################################################################
180+
# Code Coverage Configuration
181+
###############################################################################
159182

160183
# Add the coverage command(s)
161184
if(CMAKE_BUILD_TYPE)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#include <benchmark/benchmark.h>
2+
3+
#ifdef __clang__
4+
#pragma clang diagnostic ignored "-Wreturn-type"
5+
#endif
6+
7+
extern "C" {
8+
9+
extern int ExternInt;
10+
extern int ExternInt2;
11+
extern int ExternInt3;
12+
13+
inline int Add42(int x) { return x + 42; }
14+
15+
struct NotTriviallyCopyable {
16+
NotTriviallyCopyable();
17+
explicit NotTriviallyCopyable(int x) : value(x) {}
18+
NotTriviallyCopyable(NotTriviallyCopyable const&);
19+
int value;
20+
};
21+
22+
struct Large {
23+
int value;
24+
int data[2];
25+
};
26+
27+
}
28+
// CHECK-LABEL: test_with_rvalue:
29+
extern "C" void test_with_rvalue() {
30+
benchmark::DoNotOptimize(Add42(0));
31+
// CHECK: movl $42, %eax
32+
// CHECK: ret
33+
}
34+
35+
// CHECK-LABEL: test_with_large_rvalue:
36+
extern "C" void test_with_large_rvalue() {
37+
benchmark::DoNotOptimize(Large{ExternInt, {ExternInt, ExternInt}});
38+
// CHECK: movl ExternInt(%rip), %eax
39+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
40+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
41+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
42+
// CHECK: ret
43+
}
44+
45+
// CHECK-LABEL: test_with_lvalue:
46+
extern "C" void test_with_lvalue() {
47+
int x = 101;
48+
benchmark::DoNotOptimize(x);
49+
// CHECK-GNU: movl $101, %eax
50+
// CHECK-CLANG: movl $101, -{{[0-9]+}}(%rsp)
51+
// CHECK: ret
52+
}
53+
54+
// CHECK-LABEL: test_with_const_lvalue:
55+
extern "C" void test_with_const_lvalue() {
56+
const int x = 123;
57+
benchmark::DoNotOptimize(x);
58+
// CHECK: movl $123, %eax
59+
// CHECK: ret
60+
}
61+
62+
// CHECK-LABEL: test_with_large_const_lvalue:
63+
extern "C" void test_with_large_const_lvalue() {
64+
const Large L{ExternInt, {ExternInt, ExternInt}};
65+
benchmark::DoNotOptimize(L);
66+
// CHECK: movl ExternInt(%rip), %eax
67+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
68+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
69+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
70+
// CHECK: ret
71+
}
72+
73+
// CHECK-LABEL: test_with_non_trivial_const_lvalue:
74+
extern "C" void test_with_non_trivial_const_lvalue() {
75+
const NotTriviallyCopyable Obj(ExternInt);
76+
benchmark::DoNotOptimize(Obj);
77+
// CHECK: movl ExternInt(%rip), %eax
78+
// CHECK: ret
79+
}
80+
81+
// CHECK-LABEL: test_div_by_two:
82+
extern "C" int test_div_by_two(int input) {
83+
int divisor = 2;
84+
benchmark::DoNotOptimize(divisor);
85+
return input / divisor;
86+
// CHECK: movl $2, [[DEST:.*]]
87+
// CHECK: idivl [[DEST]]
88+
// CHECK: ret
89+
}
90+

test/donotoptimize_test.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ struct BitRef {
2828
int main(int, char*[]) {
2929
// this test verifies compilation of DoNotOptimize() for some types
3030

31-
char buffer8[8];
31+
char buffer8[8] = {};
3232
benchmark::DoNotOptimize(buffer8);
3333

34-
char buffer20[20];
34+
char buffer20[20] = {};
3535
benchmark::DoNotOptimize(buffer20);
3636

37-
char buffer1024[1024];
37+
char buffer1024[1024] = {};
3838
benchmark::DoNotOptimize(buffer1024);
3939
benchmark::DoNotOptimize(&buffer1024[0]);
4040

0 commit comments

Comments
 (0)