-
Notifications
You must be signed in to change notification settings - Fork 311
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SingleSource/Atomic] Add preliminary libatomic tests.
There exist atomic IR unit tests and libatomic unit tests, but neither can test the atomicity and interoperability of atomic builtins and compiler-rt's atomic library. These tests aim to approximate behaviour encountered in user code. These tests have caught issues in Clang. See llvm/llvm-project#74349 and llvm/llvm-project#73176 for LLVM changes inspired by these tests.
- Loading branch information
Showing
15 changed files
with
1,811 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
add_subdirectory(C++11) | ||
add_subdirectory(Float) | ||
if(ARCH STREQUAL "Mips") | ||
add_subdirectory(Mips) | ||
endif() | ||
add_subdirectory(Float) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
set(FP_TOLERANCE 1000000) | ||
execute_process(COMMAND ${CMAKE_C_COMPILER} --print-file-name=libclang_rt.atomic.so | ||
OUTPUT_VARIABLE _path_to_libatomic | ||
OUTPUT_STRIP_TRAILING_WHITESPACE) | ||
get_filename_component(_libatomic_dir ${_path_to_libatomic} DIRECTORY) | ||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_path_to_libatomic} -Wl,-rpath=${_libatomic_dir}") | ||
|
||
llvm_singlesource() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
//===--- big_test.cc -- Testing big (17+ byte) objects ------------ C++ -*-===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// The following text is present in each test file: | ||
// | ||
// These tests aim to capture real-world multithreaded use cases of atomic | ||
// builtins. Each test focuses on a single atomic operation. Those using | ||
// multiple operations can be compared with other tests using the same | ||
// operations to isolate bugs to a single atomic operation. | ||
// | ||
// Each test consists of a "looper" body and a test script. The test script | ||
// instantiates 10 threads, each running the looper. The loopers contend the | ||
// same memory address, performing atomic operations on it. Each looper executes | ||
// 10^6 times for a total of 10^7 operations. The resultant value in the | ||
// contended pointer is compared against a closed-form solution. It's expected | ||
// that the two values equate. | ||
// | ||
// For example, a looper that increments the shared pointer is expected to end | ||
// up with a value of 10^7. If its final value is not that, the test fails. | ||
// | ||
// Each test also tests the corresponding nonatomic operation with a separate | ||
// shared variable. Ideally, this value differs from the atomic "correct" value, | ||
// and the test can compare the two. In reality, some simpler operations (e.g. | ||
// those conducted through the ALU) can still end up with the correct answer, | ||
// even when performed nonatomically. These tests do not check the nonatomic | ||
// result, although it is still outputted to aid in debugging. | ||
// | ||
// Each test is performed on all relevant types. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This file tests atomic operations on big objects with aligned memory | ||
// addresses. | ||
// | ||
// The types tested are: bigs. | ||
// The ops tested are: xchg, cmpxchg. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include <cstdio> | ||
#include <iostream> | ||
#include <thread> | ||
#include <vector> | ||
|
||
#include "util.h" | ||
|
||
static constexpr int kBigSize = 10; | ||
struct big { | ||
int v[kBigSize]; | ||
}; | ||
|
||
// The big struct cmpxchg test is identical to the numeric cmpxchg test, except | ||
// each element of the underlying array is incremented. | ||
void looper_big_cmpxchg(big *abig, big &bbig, int success_model, | ||
int fail_model) { | ||
for (int n = 0; n < kIterations; ++n) { | ||
big desired, expected = {}; | ||
do { | ||
desired = expected; | ||
for (int k = 0; k < kBigSize; ++k) { | ||
desired.v[k]++; | ||
} | ||
} while (!__atomic_compare_exchange(abig, &expected, &desired, true, | ||
success_model, fail_model)); | ||
for (int k = 0; k < kBigSize; ++k) { | ||
bbig.v[k]++; | ||
} | ||
} | ||
} | ||
|
||
void test_big_cmpxchg() { | ||
std::vector<std::thread> pool; | ||
|
||
for (int success_model : atomic_compare_exchange_models) { | ||
for (int fail_model : atomic_compare_exchange_models) { | ||
big abig = {}; | ||
big bbig = {}; | ||
for (int n = 0; n < kThreads; ++n) { | ||
pool.emplace_back(looper_big_cmpxchg, &abig, std::ref(bbig), | ||
success_model, fail_model); | ||
} | ||
for (int n = 0; n < kThreads; ++n) { | ||
pool[n].join(); | ||
} | ||
pool.clear(); | ||
std::cout << "CMPXCHG: "; | ||
std::cout << "atomic: "; | ||
for (int n = 0; n < kBigSize; ++n) { | ||
std::cout << abig.v[n] << " "; | ||
} | ||
std::cout << "\n "; | ||
std::cout << "nonatomic: "; | ||
for (int n = 0; n < kBigSize; ++n) { | ||
std::cout << bbig.v[n] << " "; | ||
} | ||
std::cout << "\n"; | ||
for (int n = 0; n < kBigSize; ++n) { | ||
if (lt(abig.v[n], bbig.v[n]) || abig.v[n] != kExpected) { | ||
fail(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
void test_big() { | ||
printf("Testing big\n"); | ||
test_big_cmpxchg(); | ||
} | ||
|
||
int main() { | ||
printf("%d threads; %d iterations each; total of %d\n", kThreads, kIterations, | ||
kExpected); | ||
|
||
test_big(); | ||
printf("PASSED\n"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
10 threads; 1000000 iterations each; total of 10000000 | ||
Testing big | ||
CMPXCHG: atomic: 10000000 10000000 10000000 10000000 10000000 10000000 10000000 10000000 10000000 10000000 | ||
nonatomic: 9970397 9970397 9970397 9970397 9972269 9972269 9972269 9972269 9941180 9941180 | ||
PASSED | ||
exit 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
//===--- float_test.cc -- Testing aligned floating point numbers -- C++ -*-===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// The following text is present in each test file: | ||
// | ||
// These tests aim to capture real-world multithreaded use cases of atomic | ||
// builtins. Each test focuses on a single atomic operation. Those using | ||
// multiple operations can be compared with other tests using the same | ||
// operations to isolate bugs to a single atomic operation. | ||
// | ||
// Each test consists of a "looper" body and a test script. The test script | ||
// instantiates 10 threads, each running the looper. The loopers contend the | ||
// same memory address, performing atomic operations on it. Each looper executes | ||
// 10^6 times for a total of 10^7 operations. The resultant value in the | ||
// contended pointer is compared against a closed-form solution. It's expected | ||
// that the two values equate. | ||
// | ||
// For example, a looper that increments the shared pointer is expected to end | ||
// up with a value of 10^7. If its final value is not that, the test fails. | ||
// | ||
// Each test also tests the corresponding nonatomic operation with a separate | ||
// shared variable. Ideally, this value differs from the atomic "correct" value, | ||
// and the test can compare the two. In reality, some simpler operations (e.g. | ||
// those conducted through the ALU) can still end up with the correct answer, | ||
// even when performed nonatomically. These tests do not check the nonatomic | ||
// result, although it is still outputted to aid in debugging. | ||
// | ||
// Each test is performed on all relevant types. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This file tests atomic operations on floating point types with aligned | ||
// memory addresses. | ||
// | ||
// The types tested are: float, double. | ||
// The ops tested are: xchg, cmpxchg. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include <sys/stat.h> | ||
|
||
#include <cstdio> | ||
#include <thread> | ||
#include <vector> | ||
|
||
#include "numeric.h" | ||
#include "util.h" | ||
|
||
// See numeric.h for an explanation of numeric xchg tests. | ||
template <typename T> | ||
void test_float_scalar_xchg() { | ||
static constexpr T val = V >> right_shift<T>(); | ||
static constexpr T expected = val * kExpected; | ||
std::vector<std::thread> pool; | ||
|
||
for (int model : atomic_exchange_models) { | ||
T afloat = 0; | ||
T ffloat = 0; | ||
for (int n = 0; n < kThreads; ++n) { | ||
pool.emplace_back(looper_numeric_xchg_atomic<T>, &afloat, model); | ||
} | ||
for (int n = 0; n < kThreads; ++n) { | ||
pool[n].join(); | ||
} | ||
pool.clear(); | ||
for (int n = 0; n < kThreads; ++n) { | ||
pool.emplace_back(looper_numeric_xchg_nonatomic<T>, std::ref(ffloat), | ||
model); | ||
} | ||
for (int n = 0; n < kThreads; ++n) { | ||
pool[n].join(); | ||
} | ||
pool.clear(); | ||
std::cout << "SCALAR (FETCH ADD): " | ||
<< "atomic: " << afloat << " " | ||
<< "nonatomic: " << ffloat << "\n"; | ||
if (lt(afloat, ffloat) || afloat < expected * (1 - kEpsilon) || | ||
afloat > expected * (1 + kEpsilon)) { | ||
fail(); | ||
} | ||
} | ||
} | ||
|
||
// See numeric.h for an explanation of numeric cmpxchg tests. | ||
template <typename T> | ||
void test_float_scalar_cmpxchg() { | ||
static constexpr T val = V >> right_shift<T>(); | ||
static constexpr T expected = val * kExpected; | ||
std::vector<std::thread> pool; | ||
|
||
for (int success_model : atomic_compare_exchange_models) { | ||
for (int fail_model : atomic_compare_exchange_models) { | ||
T afloat = 0; | ||
T ffloat = 0; | ||
for (int n = 0; n < kThreads; ++n) { | ||
pool.emplace_back(looper_numeric_cmpxchg<T>, &afloat, std::ref(ffloat), | ||
success_model, fail_model); | ||
} | ||
for (int n = 0; n < kThreads; ++n) { | ||
pool[n].join(); | ||
} | ||
pool.clear(); | ||
std::cout << "SCALAR (FETCH ADD): " | ||
<< "atomic: " << afloat << " " | ||
<< "nonatomic: " << ffloat << "\n"; | ||
if (lt(afloat, ffloat) || afloat < expected * (1 - kEpsilon) || | ||
afloat > expected * (1 + kEpsilon)) { | ||
fail(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
void test_float() { | ||
printf("Testing float\n"); | ||
test_float_scalar_xchg<float>(); | ||
test_float_scalar_cmpxchg<float>(); | ||
|
||
printf("Testing double\n"); | ||
test_float_scalar_xchg<double>(); | ||
test_float_scalar_cmpxchg<double>(); | ||
} | ||
|
||
int main() { | ||
printf("%d threads; %d iterations each; total of %d\n", kThreads, kIterations, | ||
kExpected); | ||
|
||
test_float(); | ||
printf("PASSED\n"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
10 threads; 1000000 iterations each; total of 10000000 | ||
Testing float | ||
SCALAR (FETCH ADD): atomic: 6.50116e+08 nonatomic: 6.6e+07 | ||
SCALAR (FETCH ADD): atomic: 6.41017e+08 nonatomic: 5.8511e+08 | ||
Testing double | ||
SCALAR (FETCH ADD): atomic: 2.84596e+18 nonatomic: 2.84596e+17 | ||
SCALAR (FETCH ADD): atomic: 2.84596e+18 nonatomic: 2.5513e+18 | ||
PASSED | ||
exit 0 |
Oops, something went wrong.