mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-26 23:21:11 +00:00
[libc++] Allow running the test suite with optimizations (#68753)
This patch adds a configuration of the libc++ test suite that enables optimizations when building the tests. It also adds a new CI configuration to exercise this on a regular basis. This is added in the context of [1], which requires building with optimizations in order to hit the bug. [1]: https://github.com/llvm/llvm-project/issues/68552
This commit is contained in:
parent
07c9189fcc
commit
ca06c330fd
1
.github/workflows/libcxx-build-and-test.yaml
vendored
1
.github/workflows/libcxx-build-and-test.yaml
vendored
@ -161,6 +161,7 @@ jobs:
|
||||
'generic-no-unicode',
|
||||
'generic-no-wide-characters',
|
||||
'generic-no-rtti',
|
||||
'generic-optimized-speed',
|
||||
'generic-static',
|
||||
'generic-with_llvm_unwinder',
|
||||
# TODO Find a better place for the benchmark and bootstrapping builds to live. They're either very expensive
|
||||
|
4
libcxx/cmake/caches/Generic-optimized-speed.cmake
Normal file
4
libcxx/cmake/caches/Generic-optimized-speed.cmake
Normal file
@ -0,0 +1,4 @@
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "")
|
||||
set(LIBCXX_TEST_PARAMS "optimization=speed" CACHE STRING "")
|
||||
set(LIBCXXABI_TEST_PARAMS "${LIBCXX_TEST_PARAMS}" CACHE STRING "")
|
||||
set(LIBUNWIND_TEST_PARAMS "${LIBCXX_TEST_PARAMS}" CACHE STRING "")
|
@ -60,7 +60,7 @@ void test_aligned() {
|
||||
{
|
||||
globalMemCounter.last_new_size = 0;
|
||||
globalMemCounter.last_new_align = 0;
|
||||
T* volatile ap2 = a.allocate(11, (const void*)5);
|
||||
T* ap2 = a.allocate(11, (const void*)5);
|
||||
DoNotOptimize(ap2);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkNewCalledEq(1));
|
||||
|
@ -187,13 +187,13 @@ void test_allocator_and_new_match() {
|
||||
stats.reset();
|
||||
#if defined(NO_SIZE) && defined(NO_ALIGN)
|
||||
{
|
||||
int* x = new int(42);
|
||||
int* x = DoNotOptimize(new int(42));
|
||||
delete x;
|
||||
assert(stats.expect_plain());
|
||||
}
|
||||
stats.reset();
|
||||
{
|
||||
AlignedType* a = new AlignedType();
|
||||
AlignedType* a = DoNotOptimize(new AlignedType());
|
||||
delete a;
|
||||
assert(stats.expect_plain());
|
||||
}
|
||||
@ -202,14 +202,14 @@ void test_allocator_and_new_match() {
|
||||
stats.reset();
|
||||
#if TEST_STD_VER >= 11
|
||||
{
|
||||
int* x = new int(42);
|
||||
int* x = DoNotOptimize(new int(42));
|
||||
delete x;
|
||||
assert(stats.expect_plain());
|
||||
}
|
||||
#endif
|
||||
stats.reset();
|
||||
{
|
||||
AlignedType* a = new AlignedType();
|
||||
AlignedType* a = DoNotOptimize(new AlignedType());
|
||||
delete a;
|
||||
assert(stats.expect_align(TEST_ALIGNOF(AlignedType)));
|
||||
}
|
||||
@ -217,13 +217,13 @@ void test_allocator_and_new_match() {
|
||||
#elif defined(NO_ALIGN)
|
||||
stats.reset();
|
||||
{
|
||||
int* x = new int(42);
|
||||
int* x = DoNotOptimize(new int(42));
|
||||
delete x;
|
||||
assert(stats.expect_size(sizeof(int)));
|
||||
}
|
||||
stats.reset();
|
||||
{
|
||||
AlignedType* a = new AlignedType();
|
||||
AlignedType* a = DoNotOptimize(new AlignedType());
|
||||
delete a;
|
||||
assert(stats.expect_size(sizeof(AlignedType)));
|
||||
}
|
||||
@ -231,13 +231,13 @@ void test_allocator_and_new_match() {
|
||||
#else
|
||||
stats.reset();
|
||||
{
|
||||
int* x = new int(42);
|
||||
int* x = DoNotOptimize(new int(42));
|
||||
delete x;
|
||||
assert(stats.expect_size(sizeof(int)));
|
||||
}
|
||||
stats.reset();
|
||||
{
|
||||
AlignedType* a = new AlignedType();
|
||||
AlignedType* a = DoNotOptimize(new AlignedType());
|
||||
delete a;
|
||||
assert(stats.expect_size_align(sizeof(AlignedType),
|
||||
TEST_ALIGNOF(AlignedType)));
|
||||
|
@ -9,6 +9,10 @@
|
||||
// UNSUPPORTED: c++03, c++11, c++14
|
||||
// XFAIL: target=powerpc{{.*}}le-unknown-linux-gnu
|
||||
|
||||
// TODO: This test makes incorrect assumptions about floating point conversions.
|
||||
// See https://github.com/llvm/llvm-project/issues/74327.
|
||||
// XFAIL: optimization=speed
|
||||
|
||||
// <experimental/simd>
|
||||
//
|
||||
// [simd.class]
|
||||
|
@ -31,7 +31,7 @@ int main(int, char**) {
|
||||
const std::string s("we really really really really really really really "
|
||||
"really really long string so that we allocate");
|
||||
ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
|
||||
globalMemCounter.checkOutstandingNewEq(1));
|
||||
globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
const fs::path::string_type ps(s.begin(), s.end());
|
||||
path p(s);
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ int main(int, char**) {
|
||||
const std::string s("we really really really really really really really "
|
||||
"really really long string so that we allocate");
|
||||
ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
|
||||
globalMemCounter.checkOutstandingNewEq(1));
|
||||
globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
const fs::path::string_type ps(s.begin(), s.end());
|
||||
path p(s);
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ void operator delete(void* p) TEST_NOEXCEPT {
|
||||
|
||||
int main(int, char**) {
|
||||
new_called = delete_called = 0;
|
||||
int* x = new int[3];
|
||||
int* x = DoNotOptimize(new int[3]);
|
||||
assert(x != nullptr);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(new_called == 1);
|
||||
|
||||
|
@ -40,7 +40,7 @@ void operator delete[](void* p) TEST_NOEXCEPT {
|
||||
|
||||
int main(int, char**) {
|
||||
new_called = delete_called = 0;
|
||||
int* x = new int[3];
|
||||
int* x = DoNotOptimize(new int[3]);
|
||||
assert(x != nullptr);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(new_called == 1);
|
||||
|
||||
|
@ -51,7 +51,7 @@ int main(int, char**) {
|
||||
// Test with an overaligned type
|
||||
{
|
||||
new_called = delete_called = 0;
|
||||
OverAligned* x = new OverAligned[3];
|
||||
OverAligned* x = DoNotOptimize(new OverAligned[3]);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(static_cast<void*>(x) == DummyData);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(new_called == 1);
|
||||
|
||||
@ -62,7 +62,7 @@ int main(int, char**) {
|
||||
// Test with a type that is right on the verge of being overaligned
|
||||
{
|
||||
new_called = delete_called = 0;
|
||||
MaxAligned* x = new MaxAligned[3];
|
||||
MaxAligned* x = DoNotOptimize(new MaxAligned[3]);
|
||||
assert(x != nullptr);
|
||||
assert(new_called == 0);
|
||||
|
||||
@ -73,7 +73,7 @@ int main(int, char**) {
|
||||
// Test with a type that is clearly not overaligned
|
||||
{
|
||||
new_called = delete_called = 0;
|
||||
int* x = new int[3];
|
||||
int* x = DoNotOptimize(new int[3]);
|
||||
assert(x != nullptr);
|
||||
assert(new_called == 0);
|
||||
|
||||
|
@ -51,7 +51,7 @@ int main(int, char**) {
|
||||
// Test with an overaligned type
|
||||
{
|
||||
new_called = delete_called = 0;
|
||||
OverAligned* x = new (std::nothrow) OverAligned[3];
|
||||
OverAligned* x = DoNotOptimize(new (std::nothrow) OverAligned[3]);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(static_cast<void*>(x) == DummyData);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(new_called == 1);
|
||||
|
||||
@ -62,7 +62,7 @@ int main(int, char**) {
|
||||
// Test with a type that is right on the verge of being overaligned
|
||||
{
|
||||
new_called = delete_called = 0;
|
||||
MaxAligned* x = new (std::nothrow) MaxAligned[3];
|
||||
MaxAligned* x = DoNotOptimize(new (std::nothrow) MaxAligned[3]);
|
||||
assert(x != nullptr);
|
||||
assert(new_called == 0);
|
||||
|
||||
@ -73,7 +73,7 @@ int main(int, char**) {
|
||||
// Test with a type that is clearly not overaligned
|
||||
{
|
||||
new_called = delete_called = 0;
|
||||
int* x = new (std::nothrow) int[3];
|
||||
int* x = DoNotOptimize(new (std::nothrow) int[3]);
|
||||
assert(x != nullptr);
|
||||
assert(new_called == 0);
|
||||
|
||||
|
@ -48,7 +48,7 @@ int main(int, char**) {
|
||||
// Test with an overaligned type
|
||||
{
|
||||
new_nothrow_called = delete_called = 0;
|
||||
OverAligned* x = new (std::nothrow) OverAligned[3];
|
||||
OverAligned* x = DoNotOptimize(new (std::nothrow) OverAligned[3]);
|
||||
assert(static_cast<void*>(x) == DummyData);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(new_nothrow_called == 1);
|
||||
|
||||
@ -59,7 +59,7 @@ int main(int, char**) {
|
||||
// Test with a type that is right on the verge of being overaligned
|
||||
{
|
||||
new_nothrow_called = delete_called = 0;
|
||||
MaxAligned* x = new (std::nothrow) MaxAligned[3];
|
||||
MaxAligned* x = DoNotOptimize(new (std::nothrow) MaxAligned[3]);
|
||||
assert(x != nullptr);
|
||||
assert(new_nothrow_called == 0);
|
||||
|
||||
@ -70,7 +70,7 @@ int main(int, char**) {
|
||||
// Test with a type that is clearly not overaligned
|
||||
{
|
||||
new_nothrow_called = delete_called = 0;
|
||||
int* x = new (std::nothrow) int[3];
|
||||
int* x = DoNotOptimize(new (std::nothrow) int[3]);
|
||||
assert(x != nullptr);
|
||||
assert(new_nothrow_called == 0);
|
||||
|
||||
|
@ -46,7 +46,7 @@ void operator delete(void* p) TEST_NOEXCEPT {
|
||||
|
||||
int main(int, char**) {
|
||||
new_called = delete_called = 0;
|
||||
int* x = new (std::nothrow) int[3];
|
||||
int* x = DoNotOptimize(new (std::nothrow) int[3]);
|
||||
assert(x != nullptr);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(new_called == 1);
|
||||
|
||||
|
@ -35,7 +35,7 @@ void operator delete[](void* p) TEST_NOEXCEPT {
|
||||
|
||||
int main(int, char**) {
|
||||
new_nothrow_called = delete_called = 0;
|
||||
int* x = new (std::nothrow) int[3];
|
||||
int* x = DoNotOptimize(new (std::nothrow) int[3]);
|
||||
assert(x != nullptr);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(new_nothrow_called == 1);
|
||||
|
||||
|
@ -38,7 +38,7 @@ void operator delete(void* p) TEST_NOEXCEPT {
|
||||
|
||||
int main(int, char**) {
|
||||
new_called = delete_called = 0;
|
||||
int* x = new int(3);
|
||||
int* x = DoNotOptimize(new int(3));
|
||||
assert(x != nullptr);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(new_called == 1);
|
||||
|
||||
|
@ -50,7 +50,7 @@ int main(int, char**) {
|
||||
// Test with an overaligned type
|
||||
{
|
||||
new_called = delete_called = 0;
|
||||
OverAligned* x = new (std::nothrow) OverAligned;
|
||||
OverAligned* x = DoNotOptimize(new (std::nothrow) OverAligned);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(static_cast<void*>(x) == DummyData);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(new_called == 1);
|
||||
|
||||
@ -61,7 +61,7 @@ int main(int, char**) {
|
||||
// Test with a type that is right on the verge of being overaligned
|
||||
{
|
||||
new_called = delete_called = 0;
|
||||
MaxAligned* x = new (std::nothrow) MaxAligned;
|
||||
MaxAligned* x = DoNotOptimize(new (std::nothrow) MaxAligned);
|
||||
assert(x != nullptr);
|
||||
assert(new_called == 0);
|
||||
|
||||
@ -72,7 +72,7 @@ int main(int, char**) {
|
||||
// Test with a type that is clearly not overaligned
|
||||
{
|
||||
new_called = delete_called = 0;
|
||||
int* x = new (std::nothrow) int;
|
||||
int* x = DoNotOptimize(new (std::nothrow) int);
|
||||
assert(x != nullptr);
|
||||
assert(new_called == 0);
|
||||
|
||||
|
@ -41,7 +41,7 @@ void operator delete(void* p) TEST_NOEXCEPT {
|
||||
|
||||
int main(int, char**) {
|
||||
new_called = delete_called = 0;
|
||||
int* x = new (std::nothrow) int(3);
|
||||
int* x = DoNotOptimize(new (std::nothrow) int(3));
|
||||
assert(x != nullptr);
|
||||
ASSERT_WITH_OPERATOR_NEW_FALLBACKS(new_called == 1);
|
||||
|
||||
|
@ -69,12 +69,12 @@ int main(int, char**)
|
||||
static_assert(noexcept(swap(f1, f2)), "" );
|
||||
#endif
|
||||
assert(A::count == 2);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(2));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(2));
|
||||
RTTI_ASSERT(f1.target<A>()->id() == 1);
|
||||
RTTI_ASSERT(f2.target<A>()->id() == 2);
|
||||
swap(f1, f2);
|
||||
assert(A::count == 2);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(2));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(2));
|
||||
RTTI_ASSERT(f1.target<A>()->id() == 2);
|
||||
RTTI_ASSERT(f2.target<A>()->id() == 1);
|
||||
}
|
||||
@ -87,12 +87,12 @@ int main(int, char**)
|
||||
static_assert(noexcept(swap(f1, f2)), "" );
|
||||
#endif
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f1.target<A>()->id() == 1);
|
||||
RTTI_ASSERT(*f2.target<int(*)(int)>() == g);
|
||||
swap(f1, f2);
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(*f1.target<int(*)(int)>() == g);
|
||||
RTTI_ASSERT(f2.target<A>()->id() == 1);
|
||||
}
|
||||
@ -105,12 +105,12 @@ int main(int, char**)
|
||||
static_assert(noexcept(swap(f1, f2)), "" );
|
||||
#endif
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(*f1.target<int(*)(int)>() == g);
|
||||
RTTI_ASSERT(f2.target<A>()->id() == 1);
|
||||
swap(f1, f2);
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f1.target<A>()->id() == 1);
|
||||
RTTI_ASSERT(*f2.target<int(*)(int)>() == g);
|
||||
}
|
||||
@ -123,12 +123,12 @@ int main(int, char**)
|
||||
static_assert(noexcept(swap(f1, f2)), "" );
|
||||
#endif
|
||||
assert(A::count == 0);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(0));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(0));
|
||||
RTTI_ASSERT(*f1.target<int(*)(int)>() == g);
|
||||
RTTI_ASSERT(*f2.target<int(*)(int)>() == h);
|
||||
swap(f1, f2);
|
||||
assert(A::count == 0);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(0));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(0));
|
||||
RTTI_ASSERT(*f1.target<int(*)(int)>() == h);
|
||||
RTTI_ASSERT(*f2.target<int(*)(int)>() == g);
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ int main(int, char**)
|
||||
{
|
||||
std::function<int(int)> f = A();
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f.target<A>());
|
||||
RTTI_ASSERT(f.target<int(*)(int)>() == 0);
|
||||
}
|
||||
|
@ -57,13 +57,13 @@ int main(int, char**) {
|
||||
{
|
||||
std::function<int(int)> f = A();
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f.target<A>());
|
||||
RTTI_ASSERT(f.target<int (*)(int)>() == 0);
|
||||
std::function<int(int)> f2;
|
||||
f2 = f;
|
||||
assert(A::count == 2);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(2));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(2));
|
||||
RTTI_ASSERT(f2.target<A>());
|
||||
RTTI_ASSERT(f2.target<int (*)(int)>() == 0);
|
||||
}
|
||||
@ -125,13 +125,13 @@ int main(int, char**) {
|
||||
{
|
||||
std::function<int(int)> f = A();
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f.target<A>());
|
||||
RTTI_ASSERT(f.target<int (*)(int)>() == 0);
|
||||
std::function<int(int)> f2;
|
||||
f2 = std::move(f);
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f2.target<A>());
|
||||
RTTI_ASSERT(f2.target<int (*)(int)>() == 0);
|
||||
RTTI_ASSERT(f.target<A>() == 0);
|
||||
|
@ -64,12 +64,12 @@ int main(int, char**)
|
||||
{
|
||||
std::function<int(int)> f = A();
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f.target<A>());
|
||||
RTTI_ASSERT(f.target<int(*)(int)>() == 0);
|
||||
std::function<int(int)> f2 = f;
|
||||
assert(A::count == 2);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(2));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(2));
|
||||
RTTI_ASSERT(f2.target<A>());
|
||||
RTTI_ASSERT(f2.target<int(*)(int)>() == 0);
|
||||
}
|
||||
@ -113,7 +113,7 @@ int main(int, char**)
|
||||
{ // Test rvalue references
|
||||
std::function<int(int)> f = A();
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f.target<A>());
|
||||
RTTI_ASSERT(f.target<int(*)(int)>() == 0);
|
||||
LIBCPP_ASSERT_NOEXCEPT(std::function<int(int)>(std::move(f)));
|
||||
@ -122,7 +122,7 @@ int main(int, char**)
|
||||
#endif
|
||||
std::function<int(int)> f2 = std::move(f);
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f2.target<A>());
|
||||
RTTI_ASSERT(f2.target<int(*)(int)>() == 0);
|
||||
RTTI_ASSERT(f.target<A>() == 0);
|
||||
|
@ -57,7 +57,7 @@ int main(int, char**)
|
||||
{
|
||||
std::function<int(int)> f = A();
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f.target<A>());
|
||||
f = nullptr;
|
||||
assert(A::count == 0);
|
||||
|
@ -68,12 +68,12 @@ int main(int, char**) {
|
||||
std::function<int(int)> f1 = A(1);
|
||||
std::function<int(int)> f2 = A(2);
|
||||
assert(A::count == 2);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(2));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(2));
|
||||
RTTI_ASSERT(f1.target<A>()->id() == 1);
|
||||
RTTI_ASSERT(f2.target<A>()->id() == 2);
|
||||
f1.swap(f2);
|
||||
assert(A::count == 2);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(2));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(2));
|
||||
RTTI_ASSERT(f1.target<A>()->id() == 2);
|
||||
RTTI_ASSERT(f2.target<A>()->id() == 1);
|
||||
}
|
||||
@ -83,12 +83,12 @@ int main(int, char**) {
|
||||
std::function<int(int)> f1 = A(1);
|
||||
std::function<int(int)> f2 = g;
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f1.target<A>()->id() == 1);
|
||||
RTTI_ASSERT(*f2.target<int (*)(int)>() == g);
|
||||
f1.swap(f2);
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(*f1.target<int (*)(int)>() == g);
|
||||
RTTI_ASSERT(f2.target<A>()->id() == 1);
|
||||
}
|
||||
@ -98,12 +98,12 @@ int main(int, char**) {
|
||||
std::function<int(int)> f1 = g;
|
||||
std::function<int(int)> f2 = A(1);
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(*f1.target<int (*)(int)>() == g);
|
||||
RTTI_ASSERT(f2.target<A>()->id() == 1);
|
||||
f1.swap(f2);
|
||||
assert(A::count == 1);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(1));
|
||||
RTTI_ASSERT(f1.target<A>()->id() == 1);
|
||||
RTTI_ASSERT(*f2.target<int (*)(int)>() == g);
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ int main(int, char**)
|
||||
int i = 67;
|
||||
char c = 'e';
|
||||
std::shared_ptr<A> p = std::make_shared<A>(i, c);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(nc+1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(nc+1));
|
||||
assert(A::count == 1);
|
||||
assert(p->get_int() == 67);
|
||||
assert(p->get_char() == 'e');
|
||||
@ -116,7 +116,7 @@ int main(int, char**)
|
||||
{
|
||||
char c = 'e';
|
||||
std::shared_ptr<A> p = std::make_shared<A>(67, c);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(nc+1));
|
||||
assert(globalMemCounter.checkOutstandingNewLessThanOrEqual(nc+1));
|
||||
assert(A::count == 1);
|
||||
assert(p->get_int() == 67);
|
||||
assert(p->get_char() == 'e');
|
||||
|
@ -181,6 +181,11 @@ public:
|
||||
return disable_checking || n == outstanding_new;
|
||||
}
|
||||
|
||||
bool checkOutstandingNewLessThanOrEqual(int n) const
|
||||
{
|
||||
return disable_checking || outstanding_new <= n;
|
||||
}
|
||||
|
||||
bool checkOutstandingNewNotEq(int n) const
|
||||
{
|
||||
return disable_checking || n != outstanding_new;
|
||||
|
@ -283,27 +283,35 @@ struct is_same<T, T> { enum {value = 1}; };
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
// This function can be used to hide some objects from compiler optimizations.
|
||||
//
|
||||
// For example, this is useful to hide the result of a call to `new` and ensure
|
||||
// that the compiler doesn't elide the call to new/delete. Otherwise, elliding
|
||||
// calls to new/delete is allowed by the Standard and compilers actually do it
|
||||
// when optimizations are enabled.
|
||||
template <class Tp>
|
||||
inline
|
||||
void DoNotOptimize(Tp const& value) {
|
||||
inline Tp const& DoNotOptimize(Tp const& value) {
|
||||
asm volatile("" : : "r,m"(value) : "memory");
|
||||
return value;
|
||||
}
|
||||
|
||||
template <class Tp>
|
||||
inline void DoNotOptimize(Tp& value) {
|
||||
inline Tp& DoNotOptimize(Tp& value) {
|
||||
#if defined(__clang__)
|
||||
asm volatile("" : "+r,m"(value) : : "memory");
|
||||
#else
|
||||
asm volatile("" : "+m,r"(value) : : "memory");
|
||||
#endif
|
||||
return value;
|
||||
}
|
||||
#else
|
||||
#include <intrin.h>
|
||||
template <class Tp>
|
||||
inline void DoNotOptimize(Tp const& value) {
|
||||
inline Tp const& DoNotOptimize(Tp const& value) {
|
||||
const volatile void* volatile unused = __builtin_addressof(value);
|
||||
static_cast<void>(unused);
|
||||
_ReadWriteBarrier();
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -522,6 +522,11 @@ generic-abi-unstable)
|
||||
generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-abi-unstable.cmake"
|
||||
check-runtimes
|
||||
;;
|
||||
generic-optimized-speed)
|
||||
clean
|
||||
generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-optimized-speed.cmake"
|
||||
check-runtimes
|
||||
;;
|
||||
apple-system)
|
||||
clean
|
||||
|
||||
|
@ -11,7 +11,7 @@ import shlex
|
||||
from pathlib import Path
|
||||
|
||||
from libcxx.test.dsl import *
|
||||
from libcxx.test.features import _isMSVC
|
||||
from libcxx.test.features import _isClang, _isAppleClang, _isGCC, _isMSVC
|
||||
|
||||
|
||||
_warningFlags = [
|
||||
@ -88,6 +88,28 @@ def getStdFlag(cfg, std):
|
||||
return None
|
||||
|
||||
|
||||
def getSpeedOptimizationFlag(cfg):
|
||||
if _isClang(cfg) or _isAppleClang(cfg) or _isGCC(cfg):
|
||||
return "-O3"
|
||||
elif _isMSVC(cfg):
|
||||
return "/O2"
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"Can't figure out what compiler is used in the configuration"
|
||||
)
|
||||
|
||||
|
||||
def getSizeOptimizationFlag(cfg):
|
||||
if _isClang(cfg) or _isAppleClang(cfg) or _isGCC(cfg):
|
||||
return "-Os"
|
||||
elif _isMSVC(cfg):
|
||||
return "/O1"
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"Can't figure out what compiler is used in the configuration"
|
||||
)
|
||||
|
||||
|
||||
# fmt: off
|
||||
DEFAULT_PARAMETERS = [
|
||||
Parameter(
|
||||
@ -118,6 +140,18 @@ DEFAULT_PARAMETERS = [
|
||||
AddCompileFlag(lambda cfg: getStdFlag(cfg, std)),
|
||||
],
|
||||
),
|
||||
Parameter(
|
||||
name="optimization",
|
||||
choices=["none", "speed", "size"],
|
||||
type=str,
|
||||
help="The optimization level to use when compiling the test suite.",
|
||||
default="none",
|
||||
actions=lambda opt: filter(None, [
|
||||
AddCompileFlag(lambda cfg: getSpeedOptimizationFlag(cfg)) if opt == "speed" else None,
|
||||
AddCompileFlag(lambda cfg: getSizeOptimizationFlag(cfg)) if opt == "size" else None,
|
||||
AddFeature(f'optimization={opt}'),
|
||||
]),
|
||||
),
|
||||
Parameter(
|
||||
name="enable_modules",
|
||||
choices=["none", "clang", "clang-lsv"],
|
||||
|
@ -21,7 +21,8 @@
|
||||
#define EXPECTED_NUM_FRAMES 50
|
||||
#define NUM_FRAMES_UPPER_BOUND 100
|
||||
|
||||
_Unwind_Reason_Code callback(_Unwind_Context *context, void *cnt) {
|
||||
__attribute__((noinline)) _Unwind_Reason_Code callback(_Unwind_Context *context,
|
||||
void *cnt) {
|
||||
(void)context;
|
||||
int *i = (int *)cnt;
|
||||
++*i;
|
||||
@ -31,7 +32,7 @@ _Unwind_Reason_Code callback(_Unwind_Context *context, void *cnt) {
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
void test_backtrace() {
|
||||
__attribute__((noinline)) void test_backtrace() {
|
||||
int n = 0;
|
||||
_Unwind_Backtrace(&callback, &n);
|
||||
if (n < EXPECTED_NUM_FRAMES) {
|
||||
@ -39,17 +40,34 @@ void test_backtrace() {
|
||||
}
|
||||
}
|
||||
|
||||
int test(int i) {
|
||||
// These functions are effectively the same, but we have to be careful to avoid
|
||||
// unwanted optimizations that would mess with the number of frames we expect.
|
||||
// Surprisingly, slapping `noinline` is not sufficient -- we also have to avoid
|
||||
// writing the function in a way that the compiler can easily spot tail
|
||||
// recursion.
|
||||
__attribute__((noinline)) int test1(int i);
|
||||
__attribute__((noinline)) int test2(int i);
|
||||
|
||||
__attribute__((noinline)) int test1(int i) {
|
||||
if (i == 0) {
|
||||
test_backtrace();
|
||||
return 0;
|
||||
} else {
|
||||
return i + test(i - 1);
|
||||
return i + test2(i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((noinline)) int test2(int i) {
|
||||
if (i == 0) {
|
||||
test_backtrace();
|
||||
return 0;
|
||||
} else {
|
||||
return i + test1(i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
int total = test(50);
|
||||
int total = test1(50);
|
||||
assert(total == 1275);
|
||||
return 0;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
#include <libunwind.h>
|
||||
|
||||
void test_unw_resume() {
|
||||
__attribute__((noinline)) void test_unw_resume() {
|
||||
unw_context_t context;
|
||||
unw_cursor_t cursor;
|
||||
|
||||
|
@ -28,7 +28,7 @@ _Unwind_Reason_Code frame_handler(struct _Unwind_Context* ctx, void* arg) {
|
||||
(void)arg;
|
||||
Dl_info info = { 0, 0, 0, 0 };
|
||||
|
||||
// Unwind until the main is reached, above frames deeped on the platform and
|
||||
// Unwind until the main is reached, above frames depend on the platform and
|
||||
// architecture.
|
||||
if (dladdr(reinterpret_cast<void *>(_Unwind_GetIP(ctx)), &info) &&
|
||||
info.dli_sname && !strcmp("main", info.dli_sname)) {
|
||||
@ -43,18 +43,22 @@ void signal_handler(int signum) {
|
||||
_Exit(-1);
|
||||
}
|
||||
|
||||
__attribute__((noinline)) void crashing_leaf_func(void) {
|
||||
__attribute__((noinline)) void crashing_leaf_func(int do_trap) {
|
||||
// libunwind searches for the address before the return address which points
|
||||
// to the trap instruction. NOP guarantees the trap instruction is not the
|
||||
// first instruction of the function.
|
||||
// We should keep this here for other unwinders that also decrement pc.
|
||||
__asm__ __volatile__("nop");
|
||||
__builtin_trap();
|
||||
// to the trap instruction. We make the trap conditional and prevent inlining
|
||||
// of the function to ensure that the compiler doesn't remove the `ret`
|
||||
// instruction altogether.
|
||||
//
|
||||
// It's also important that the trap instruction isn't the first instruction
|
||||
// in the function (which it isn't because of the branch) for other unwinders
|
||||
// that also decrement pc.
|
||||
if (do_trap)
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
signal(SIGTRAP, signal_handler);
|
||||
signal(SIGILL, signal_handler);
|
||||
crashing_leaf_func();
|
||||
crashing_leaf_func(1);
|
||||
return -2;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user