Use DoNotOptimize to prevent new/delete elision.

The new/delete tests, in particular those which test replacement
functions, often fail when the optimizer is enabled because the
calls to new/delete may be optimized away, regardless of their side-effects.

This patch converts the tests to use DoNotOptimize in order to prevent
the elision.

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@328245 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Eric Fiselier
2018-03-22 21:28:09 +00:00
parent b7f27d421f
commit b06c8f07b2
17 changed files with 74 additions and 47 deletions

View File

@@ -69,31 +69,32 @@ void operator delete [] (void* p, std::align_val_t) TEST_NOEXCEPT
struct alignas(OverAligned) A {}; struct alignas(OverAligned) A {};
struct alignas(std::max_align_t) B {}; struct alignas(std::max_align_t) B {};
B* volatile b; // Escape the memory
A* volatile a;
int main() int main()
{ {
reset(); reset();
{ {
b = new B[2]; B *b = new B[2];
DoNotOptimize(b);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
assert(0 == aligned_delete_called); assert(0 == aligned_delete_called);
delete [] b; delete [] b;
DoNotOptimize(b);
assert(1 == unsized_delete_called); assert(1 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
assert(0 == aligned_delete_called); assert(0 == aligned_delete_called);
} }
reset(); reset();
{ {
a = new A[2]; A *a = new A[2];
DoNotOptimize(a);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
assert(0 == aligned_delete_called); assert(0 == aligned_delete_called);
delete [] a; delete [] a;
DoNotOptimize(a);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
assert(1 == aligned_delete_called); assert(1 == aligned_delete_called);

View File

@@ -53,7 +53,9 @@ void* operator new[](std::size_t s, std::align_val_t a) TEST_THROW_SPEC(std::bad
assert(s <= sizeof(DummyData)); assert(s <= sizeof(DummyData));
assert(static_cast<std::size_t>(a) == OverAligned); assert(static_cast<std::size_t>(a) == OverAligned);
++new_called; ++new_called;
return DummyData; void *Ret = DummyData;
DoNotOptimize(Ret);
return Ret;
} }
void operator delete[](void* p, std::align_val_t) TEST_NOEXCEPT void operator delete[](void* p, std::align_val_t) TEST_NOEXCEPT
@@ -61,6 +63,7 @@ void operator delete[](void* p, std::align_val_t) TEST_NOEXCEPT
assert(new_called == 1); assert(new_called == 1);
--new_called; --new_called;
assert(p == DummyData); assert(p == DummyData);
DoNotOptimize(p);
} }

View File

@@ -41,8 +41,8 @@ int main()
std::set_new_handler(my_new_handler); std::set_new_handler(my_new_handler);
try try
{ {
void* volatile vp = operator new[] (std::numeric_limits<std::size_t>::max()); void* vp = operator new[] (std::numeric_limits<std::size_t>::max());
((void)vp); DoNotOptimize(vp);
assert(false); assert(false);
} }
catch (std::bad_alloc&) catch (std::bad_alloc&)
@@ -55,8 +55,10 @@ int main()
} }
#endif #endif
A* ap = new A[3]; A* ap = new A[3];
DoNotOptimize(ap);
assert(ap); assert(ap);
assert(A_constructed == 3); assert(A_constructed == 3);
delete [] ap; delete [] ap;
DoNotOptimize(ap);
assert(A_constructed == 0); assert(A_constructed == 0);
} }

View File

@@ -42,7 +42,8 @@ int main()
try try
#endif #endif
{ {
void*volatile vp = operator new [] (std::numeric_limits<std::size_t>::max(), std::nothrow); void* vp = operator new [] (std::numeric_limits<std::size_t>::max(), std::nothrow);
DoNotOptimize(vp);
assert(new_handler_called == 1); assert(new_handler_called == 1);
assert(vp == 0); assert(vp == 0);
} }
@@ -53,8 +54,10 @@ int main()
} }
#endif #endif
A* ap = new(std::nothrow) A[3]; A* ap = new(std::nothrow) A[3];
DoNotOptimize(ap);
assert(ap); assert(ap);
assert(A_constructed == 3); assert(A_constructed == 3);
delete [] ap; delete [] ap;
DoNotOptimize(ap);
assert(A_constructed == 0); assert(A_constructed == 0);
} }

View File

@@ -36,7 +36,7 @@ void operator delete(void* p) TEST_NOEXCEPT
std::free(p); std::free(p);
} }
volatile int A_constructed = 0; int A_constructed = 0;
struct A struct A
{ {
@@ -44,15 +44,15 @@ struct A
~A() {--A_constructed;} ~A() {--A_constructed;}
}; };
A* volatile ap;
int main() int main()
{ {
ap = new (std::nothrow) A[3]; A *ap = new (std::nothrow) A[3];
DoNotOptimize(ap);
assert(ap); assert(ap);
assert(A_constructed == 3); assert(A_constructed == 3);
assert(new_called); assert(new_called);
delete [] ap; delete [] ap;
DoNotOptimize(ap);
assert(A_constructed == 0); assert(A_constructed == 0);
assert(!new_called); assert(!new_called);
} }

View File

@@ -21,7 +21,7 @@
#include "test_macros.h" #include "test_macros.h"
volatile int new_called = 0; int new_called = 0;
void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc) void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc)
{ {
@@ -45,15 +45,15 @@ struct A
~A() {--A_constructed;} ~A() {--A_constructed;}
}; };
A* volatile ap;
int main() int main()
{ {
ap = new A[3]; A *ap = new A[3];
DoNotOptimize(ap);
assert(ap); assert(ap);
assert(A_constructed == 3); assert(A_constructed == 3);
assert(new_called == 1); assert(new_called == 1);
delete [] ap; delete [] ap;
DoNotOptimize(ap);
assert(A_constructed == 0); assert(A_constructed == 0);
assert(new_called == 0); assert(new_called == 0);
} }

View File

@@ -46,15 +46,15 @@ void operator delete[](void* p, const std::nothrow_t&) TEST_NOEXCEPT
// selected. // selected.
struct A { ~A() {} }; struct A { ~A() {} };
A *volatile x;
int main() int main()
{ {
x = new A[3]; A *x = new A[3];
DoNotOptimize(x);
assert(0 == delete_called); assert(0 == delete_called);
assert(0 == delete_nothrow_called); assert(0 == delete_nothrow_called);
delete [] x; delete [] x;
DoNotOptimize(x);
assert(1 == delete_called); assert(1 == delete_called);
assert(0 == delete_nothrow_called); assert(0 == delete_nothrow_called);
} }

View File

@@ -68,31 +68,32 @@ void operator delete(void* p, std::align_val_t) TEST_NOEXCEPT
struct alignas(OverAligned) A {}; struct alignas(OverAligned) A {};
struct alignas(std::max_align_t) B {}; struct alignas(std::max_align_t) B {};
B* volatile bp;
A* volatile ap;
int main() int main()
{ {
reset(); reset();
{ {
bp = new B; B *bp = new B;
DoNotOptimize(bp);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
assert(0 == aligned_delete_called); assert(0 == aligned_delete_called);
delete bp; delete bp;
DoNotOptimize(bp);
assert(1 == unsized_delete_called); assert(1 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
assert(0 == aligned_delete_called); assert(0 == aligned_delete_called);
} }
reset(); reset();
{ {
ap = new A; A *ap = new A;
DoNotOptimize(ap);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
assert(0 == aligned_delete_called); assert(0 == aligned_delete_called);
delete ap; delete ap;
DoNotOptimize(ap);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
assert(1 == aligned_delete_called); assert(1 == aligned_delete_called);

View File

@@ -53,7 +53,9 @@ void* operator new(std::size_t s, std::align_val_t a) TEST_THROW_SPEC(std::bad_a
assert(s <= sizeof(DummyData)); assert(s <= sizeof(DummyData));
assert(static_cast<std::size_t>(a) == OverAligned); assert(static_cast<std::size_t>(a) == OverAligned);
++new_called; ++new_called;
return DummyData; void *Ret = DummyData;
DoNotOptimize(Ret);
return Ret;
} }
void operator delete(void* p, std::align_val_t) TEST_NOEXCEPT void operator delete(void* p, std::align_val_t) TEST_NOEXCEPT
@@ -61,6 +63,7 @@ void operator delete(void* p, std::align_val_t) TEST_NOEXCEPT
assert(new_called == 1); assert(new_called == 1);
--new_called; --new_called;
assert(p == DummyData); assert(p == DummyData);
DoNotOptimize(DummyData);
} }

View File

@@ -44,15 +44,15 @@ struct A
~A() {A_constructed = false;} ~A() {A_constructed = false;}
}; };
A* volatile ap;
int main() int main()
{ {
ap = new (std::nothrow) A; A *ap = new (std::nothrow) A;
DoNotOptimize(ap);
assert(ap); assert(ap);
assert(A_constructed); assert(A_constructed);
assert(new_called); assert(new_called);
delete ap; delete ap;
DoNotOptimize(ap);
assert(!A_constructed); assert(!A_constructed);
assert(!new_called); assert(!new_called);
} }

View File

@@ -43,15 +43,15 @@ struct A
~A() {A_constructed = false;} ~A() {A_constructed = false;}
}; };
A *volatile ap;
int main() int main()
{ {
ap = new A; A *ap = new A;
DoNotOptimize(ap);
assert(ap); assert(ap);
assert(A_constructed); assert(A_constructed);
assert(new_called); assert(new_called);
delete ap; delete ap;
DoNotOptimize(ap);
assert(!A_constructed); assert(!A_constructed);
assert(!new_called); assert(!new_called);
} }

View File

@@ -44,16 +44,16 @@ void operator delete(void* p, std::size_t) TEST_NOEXCEPT
std::free(p); std::free(p);
} }
int *volatile x;
int main() int main()
{ {
x = new int(42); int *x = new int(42);
DoNotOptimize(x);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
assert(0 == sized_delete_called); assert(0 == sized_delete_called);
delete x; delete x;
DoNotOptimize(x);
assert(1 == unsized_delete_called); assert(1 == unsized_delete_called);
assert(0 == sized_delete_called); assert(0 == sized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);

View File

@@ -49,16 +49,16 @@ void operator delete(void* p, std::size_t) TEST_NOEXCEPT
std::free(p); std::free(p);
} }
int *volatile x;
int main() int main()
{ {
x = new int(42); int *x = new int(42);
DoNotOptimize(x);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
assert(0 == sized_delete_called); assert(0 == sized_delete_called);
delete x; delete x;
DoNotOptimize(x);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(1 == sized_delete_called); assert(1 == sized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);

View File

@@ -35,15 +35,15 @@ void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT
std::free(p); std::free(p);
} }
int* volatile x;
int main() int main()
{ {
x = new int(42); int *x = new int(42);
DoNotOptimize(x);
assert(0 == delete_called); assert(0 == delete_called);
assert(0 == delete_nothrow_called); assert(0 == delete_nothrow_called);
delete x; delete x;
DoNotOptimize(x);
assert(1 == delete_called); assert(1 == delete_called);
assert(0 == delete_nothrow_called); assert(0 == delete_nothrow_called);
} }

View File

@@ -62,16 +62,16 @@ void operator delete(void* p, std::size_t) TEST_NOEXCEPT
std::free(p); std::free(p);
} }
int* volatile x;
int main() int main()
{ {
x = new int(42); int *x = new int(42);
DoNotOptimize(x);
assert(0 == sized_delete_called); assert(0 == sized_delete_called);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);
delete x; delete x;
DoNotOptimize(x);
assert(1 == sized_delete_called); assert(1 == sized_delete_called);
assert(0 == unsized_delete_called); assert(0 == unsized_delete_called);
assert(0 == unsized_delete_nothrow_called); assert(0 == unsized_delete_nothrow_called);

View File

@@ -14,6 +14,7 @@
#include <memory> #include <memory>
#include <cassert> #include <cassert>
#include <iostream>
#include "test_macros.h" #include "test_macros.h"
#include "count_new.hpp" #include "count_new.hpp"
@@ -59,7 +60,8 @@ void test_aligned() {
assert(T::constructed == 0); assert(T::constructed == 0);
globalMemCounter.last_new_size = 0; globalMemCounter.last_new_size = 0;
globalMemCounter.last_new_align = 0; globalMemCounter.last_new_align = 0;
T* volatile ap = a.allocate(3); T* ap = a.allocate(3);
DoNotOptimize(ap);
assert(globalMemCounter.checkOutstandingNewEq(1)); assert(globalMemCounter.checkOutstandingNewEq(1));
assert(globalMemCounter.checkNewCalledEq(1)); assert(globalMemCounter.checkNewCalledEq(1));
assert(globalMemCounter.checkAlignedNewCalledEq(ExpectAligned)); assert(globalMemCounter.checkAlignedNewCalledEq(ExpectAligned));
@@ -79,6 +81,7 @@ void test_aligned() {
globalMemCounter.last_new_size = 0; globalMemCounter.last_new_size = 0;
globalMemCounter.last_new_align = 0; globalMemCounter.last_new_align = 0;
T* volatile ap2 = a.allocate(11, (const void*)5); T* volatile ap2 = a.allocate(11, (const void*)5);
DoNotOptimize(ap2);
assert(globalMemCounter.checkOutstandingNewEq(1)); assert(globalMemCounter.checkOutstandingNewEq(1));
assert(globalMemCounter.checkNewCalledEq(1)); assert(globalMemCounter.checkNewCalledEq(1));
assert(globalMemCounter.checkAlignedNewCalledEq(ExpectAligned)); assert(globalMemCounter.checkAlignedNewCalledEq(ExpectAligned));
@@ -87,6 +90,7 @@ void test_aligned() {
assert(T::constructed == 0); assert(T::constructed == 0);
globalMemCounter.last_delete_align = 0; globalMemCounter.last_delete_align = 0;
a.deallocate(ap2, 11); a.deallocate(ap2, 11);
DoNotOptimize(ap2);
assert(globalMemCounter.checkOutstandingNewEq(0)); assert(globalMemCounter.checkOutstandingNewEq(0));
assert(globalMemCounter.checkDeleteCalledEq(1)); assert(globalMemCounter.checkDeleteCalledEq(1));
assert(globalMemCounter.checkAlignedDeleteCalledEq(ExpectAligned)); assert(globalMemCounter.checkAlignedDeleteCalledEq(ExpectAligned));

View File

@@ -221,8 +221,18 @@ struct is_same<T, T> { enum {value = 1}; };
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
template <class Tp> template <class Tp>
inline void DoNotOptimize(Tp const& value) { inline
asm volatile("" : : "g"(value) : "memory"); void DoNotOptimize(Tp const& value) {
asm volatile("" : : "r,m"(value) : "memory");
}
template <class Tp>
inline void DoNotOptimize(Tp& value) {
#if defined(__clang__)
asm volatile("" : "+r,m"(value) : : "memory");
#else
asm volatile("" : "+m,r"(value) : : "memory");
#endif
} }
#else #else
#include <intrin.h> #include <intrin.h>