Skip to content

Commit 428d4ca

Browse files
authored
Merge 20b19bd into 3924ee7
2 parents 3924ee7 + 20b19bd commit 428d4ca

File tree

10 files changed

+381
-14
lines changed

10 files changed

+381
-14
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: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ matrix:
3434
env:
3535
- INSTALL_GCC6_FROM_PPA=1
3636
- COMPILER=g++-6 C_COMPILER=gcc-6 BUILD_TYPE=Debug
37+
- ENABLE_SANITIZER=1
3738
- EXTRA_FLAGS="-fno-omit-frame-pointer -g -O2 -fsanitize=undefined,address -fuse-ld=gold"
3839
- compiler: clang
3940
env: COMPILER=clang++ C_COMPILER=clang BUILD_TYPE=Debug
@@ -91,6 +92,7 @@ matrix:
9192
env:
9293
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
9394
- LIBCXX_BUILD=1 LIBCXX_SANITIZER="Undefined;Address"
95+
- ENABLE_SANITIZER=1
9496
- EXTRA_FLAGS="-stdlib=libc++ -g -O2 -fno-omit-frame-pointer -fsanitize=undefined,address -fno-sanitize-recover=all"
9597
- UBSAN_OPTIONS=print_stacktrace=1
9698
# Clang w/ libc++ and MSAN
@@ -102,6 +104,7 @@ matrix:
102104
env:
103105
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
104106
- LIBCXX_BUILD=1 LIBCXX_SANITIZER=MemoryWithOrigins
107+
- ENABLE_SANITIZER=1
105108
- EXTRA_FLAGS="-stdlib=libc++ -g -O2 -fno-omit-frame-pointer -fsanitize=memory -fsanitize-memory-track-origins"
106109
# Clang w/ libc++ and MSAN
107110
- compiler: clang
@@ -112,8 +115,8 @@ matrix:
112115
env:
113116
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=RelWithDebInfo
114117
- LIBCXX_BUILD=1 LIBCXX_SANITIZER=Thread
118+
- ENABLE_SANITIZER=1
115119
- EXTRA_FLAGS="-stdlib=libc++ -g -O2 -fno-omit-frame-pointer -fsanitize=thread -fno-sanitize-recover=all"
116-
117120
- os: osx
118121
osx_image: xcode8.3
119122
compiler: clang
@@ -131,23 +134,32 @@ matrix:
131134
- COMPILER=g++-7 C_COMPILER=gcc-7 BUILD_TYPE=Debug
132135

133136
before_script:
134-
- if [ -z "$BUILD_32_BITS" ]; then
135-
export BUILD_32_BITS=OFF && echo disabling 32 bit build;
136-
fi
137137
- if [ -n "${LIBCXX_BUILD}" ]; then
138138
source .travis-libcxx-setup.sh;
139139
fi
140+
- if [ -n "${ENABLE_SANITIZER}" ]; then
141+
export EXTRA_OPTIONS="-DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF";
142+
else
143+
export EXTRA_OPTIONS="";
144+
fi
140145
- mkdir -p build && cd build
141146

142147
before_install:
148+
- if [ -z "$BUILD_32_BITS" ]; then
149+
export BUILD_32_BITS=OFF && echo disabling 32 bit build;
150+
fi
143151
- if [ -n "${INSTALL_GCC6_FROM_PPA}" ]; then
144152
sudo add-apt-repository -y "ppa:ubuntu-toolchain-r/test";
145153
sudo apt-get update --option Acquire::Retries=100 --option Acquire::http::Timeout="60";
146154
fi
147155

148156
install:
149157
- if [ -n "${INSTALL_GCC6_FROM_PPA}" ]; then
150-
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install g++-6;
158+
sudo -E apt-get -yq --no-install-suggests --no-install-recommends install g++-6;
159+
fi
160+
- if [ "${TRAVIS_OS_NAME}" == "linux" -a "${BUILD_32_BITS}" == "OFF" ]; then
161+
sudo -E apt-get -y --no-install-suggests --no-install-recommends install llvm-3.9-tools;
162+
sudo cp /usr/lib/llvm-3.9/bin/FileCheck /usr/local/bin/;
151163
fi
152164
- if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then
153165
PATH=~/.local/bin:${PATH};
@@ -161,7 +173,7 @@ install:
161173
fi
162174

163175
script:
164-
- cmake -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_CXX_FLAGS="${EXTRA_FLAGS}" -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_BUILD_32_BITS=${BUILD_32_BITS} ..
176+
- cmake -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_CXX_FLAGS="${EXTRA_FLAGS}" -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_BUILD_32_BITS=${BUILD_32_BITS} ${EXTRA_OPTIONS} ..
165177
- make
166178
- ctest -C ${BUILD_TYPE} --output-on-failure
167179

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: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,15 +303,32 @@ BENCHMARK_UNUSED static int stream_init_anchor = InitializeStreams();
303303
// See: https://youtu.be/nXaxk27zwlk?t=2441
304304
#ifndef BENCHMARK_HAS_NO_INLINE_ASSEMBLY
305305
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.
306+
inline BENCHMARK_ALWAYS_INLINE
307+
void DoNotOptimize(Tp const& value) {
308+
asm volatile("" : : "r,m"(value) : "memory");
309+
}
310+
311+
template <class Tp>
312+
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
309313
#if defined(__clang__)
310-
asm volatile("" : : "g"(value) : "memory");
314+
asm volatile("" : "+r,m"(value) : : "memory");
311315
#else
312-
asm volatile("" : : "i,r,m"(value) : "memory");
316+
asm volatile("" : "+m,r"(value) : : "memory");
313317
#endif
314318
}
319+
320+
#if defined(BENCHMARK_HAS_CXX11)
321+
template <class Tp>
322+
inline BENCHMARK_ALWAYS_INLINE
323+
typename std::enable_if<!std::is_lvalue_reference<Tp>::value, void>::type
324+
DoNotOptimize(Tp&& value) {
325+
static_assert(!std::is_lvalue_reference<Tp>::value, "");
326+
static_assert(!std::is_const<Tp>::value, "");
327+
asm volatile("" : : "r,m"(value) : "memory");
328+
}
329+
330+
template <class Tp> void DoNotOptimize(Tp const&& value) = delete;
331+
#endif
315332
// Force the compiler to flush pending writes to global memory. Acts as an
316333
// effective read/write barrier
317334
inline BENCHMARK_ALWAYS_INLINE void ClobberMemory() {

test/AssemblyTests.cmake

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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_target(copy_${name} ALL
28+
COMMAND ${PROJECT_SOURCE_DIR}/tools/strip_asm.py
29+
$<TARGET_OBJECTS:${name}>
30+
${ASM_OUTPUT_FILE}
31+
BYPRODUCTS ${ASM_OUTPUT_FILE})
32+
add_dependencies(copy_${name} ${name})
33+
if (NOT ARG_CHECK_PREFIXES)
34+
set(ARG_CHECK_PREFIXES "CHECK")
35+
endif()
36+
foreach(prefix ${ARG_CHECK_PREFIXES})
37+
add_test(NAME run_${name}_${prefix}
38+
COMMAND
39+
${LLVM_FILECHECK_EXE} ${name}.cc
40+
--input-file=${ASM_OUTPUT_FILE}
41+
--check-prefixes=CHECK,CHECK-${ASM_TEST_COMPILER}
42+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
43+
endforeach()
44+
endmacro()
45+

test/CMakeLists.txt

Lines changed: 24 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,24 @@ 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+
add_filecheck_test(state_assembly_test)
176+
endif()
177+
178+
179+
180+
###############################################################################
181+
# Code Coverage Configuration
182+
###############################################################################
159183

160184
# Add the coverage command(s)
161185
if(CMAKE_BUILD_TYPE)
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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_non_trivial_rvalue:
46+
extern "C" void test_with_non_trivial_rvalue() {
47+
benchmark::DoNotOptimize(NotTriviallyCopyable(ExternInt));
48+
// CHECK: movl ExternInt(%rip), %eax
49+
// CHECK: ret
50+
}
51+
52+
// CHECK-LABEL: test_with_lvalue:
53+
extern "C" void test_with_lvalue() {
54+
int x = 101;
55+
benchmark::DoNotOptimize(x);
56+
// CHECK-GNU: movl $101, %eax
57+
// CHECK-CLANG: movl $101, -{{[0-9]+}}(%rsp)
58+
// CHECK: ret
59+
}
60+
61+
// CHECK-LABEL: test_with_large_lvalue:
62+
extern "C" void test_with_large_lvalue() {
63+
Large L{ExternInt, {ExternInt, ExternInt}};
64+
benchmark::DoNotOptimize(L);
65+
// CHECK: movl ExternInt(%rip), %eax
66+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
67+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
68+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
69+
// CHECK: ret
70+
}
71+
72+
// CHECK-LABEL: test_with_non_trivial_lvalue:
73+
extern "C" void test_with_non_trivial_lvalue() {
74+
NotTriviallyCopyable NTC(ExternInt);
75+
benchmark::DoNotOptimize(NTC);
76+
// CHECK: movl ExternInt(%rip), %eax
77+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
78+
// CHECK: ret
79+
}
80+
81+
82+
// CHECK-LABEL: test_with_const_lvalue:
83+
extern "C" void test_with_const_lvalue() {
84+
const int x = 123;
85+
benchmark::DoNotOptimize(x);
86+
// CHECK: movl $123, %eax
87+
// CHECK: ret
88+
}
89+
90+
// CHECK-LABEL: test_with_large_const_lvalue:
91+
extern "C" void test_with_large_const_lvalue() {
92+
const Large L{ExternInt, {ExternInt, ExternInt}};
93+
benchmark::DoNotOptimize(L);
94+
// CHECK: movl ExternInt(%rip), %eax
95+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
96+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
97+
// CHECK: movl %eax, -{{[0-9]+}}(%rsp)
98+
// CHECK: ret
99+
}
100+
101+
// CHECK-LABEL: test_with_non_trivial_const_lvalue:
102+
extern "C" void test_with_non_trivial_const_lvalue() {
103+
const NotTriviallyCopyable Obj(ExternInt);
104+
benchmark::DoNotOptimize(Obj);
105+
// CHECK: movl ExternInt(%rip), %eax
106+
// CHECK: ret
107+
}
108+
109+
// CHECK-LABEL: test_div_by_two:
110+
extern "C" int test_div_by_two(int input) {
111+
int divisor = 2;
112+
benchmark::DoNotOptimize(divisor);
113+
return input / divisor;
114+
// CHECK: movl $2, [[DEST:.*]]
115+
// CHECK: idivl [[DEST]]
116+
// CHECK: ret
117+
}
118+

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)