Improve codegen for checks.

Use fail fast in release to avoid stack frame for error reporting.

Scope check_client macro.
This commit is contained in:
Matthew Parkinson
2021-11-24 14:34:26 +00:00
committed by Matthew Parkinson
parent a8ef963ed7
commit c299826f58
4 changed files with 37 additions and 26 deletions

View File

@@ -3,6 +3,7 @@
#if defined(_MSC_VER) && !defined(__clang__)
// 28 is FAST_FAIL_INVALID_BUFFER_ACCESS. Not using the symbolic constant to
// avoid depending on winnt.h
# include <intrin.h> // for __fastfail
# define SNMALLOC_FAST_FAIL() __fastfail(28)
# define ALWAYSINLINE __forceinline
# define NOINLINE __declspec(noinline)
@@ -141,32 +142,42 @@ namespace snmalloc
# endif
#endif
inline SNMALLOC_FAST_PATH void check_client_error(const char* const str)
{
//[[clang::musttail]]
return snmalloc::error(str);
}
inline SNMALLOC_FAST_PATH void
check_client_impl(bool test, const char* const str)
{
if (SNMALLOC_UNLIKELY(!test))
check_client_error(str);
}
#ifdef SNMALLOC_CHECK_CLIENT
# define check_client(test, str) check_client_impl(test, str)
#else
# define check_client(test, str)
#endif
namespace snmalloc
{
template<typename... Args>
SNMALLOC_FAST_PATH_INLINE void UNUSED(Args&&...)
{}
inline SNMALLOC_FAST_PATH void check_client_error(const char* const str)
{
//[[clang::musttail]]
return snmalloc::error(str);
}
inline SNMALLOC_FAST_PATH void
check_client_impl(bool test, const char* const str)
{
if (SNMALLOC_UNLIKELY(!test))
{
#ifdef NDEBUG
UNUSED(str);
SNMALLOC_FAST_FAIL();
#else
check_client_error(str);
#endif
}
}
#ifdef SNMALLOC_CHECK_CLIENT
static constexpr bool CHECK_CLIENT = true;
#else
static constexpr bool CHECK_CLIENT = false;
#endif
template<typename... Args>
SNMALLOC_FAST_PATH_INLINE void UNUSED(Args&&...)
{}
} // namespace snmalloc
#ifdef SNMALLOC_CHECK_CLIENT
# define snmalloc_check_client(test, str) \
snmalloc::check_client_impl(test, str)
#else
# define snmalloc_check_client(test, str)
#endif

View File

@@ -379,7 +379,7 @@ namespace snmalloc
alloc_classes[sizeclass].unused--;
// TODO delay the clear to the next user of the slab, or teardown so
// don't touch the cache lines at this point in check_client.
// don't touch the cache lines at this point in snmalloc_check_client.
auto chunk_record = clear_slab(meta, sizeclass);
ChunkAllocator::dealloc<SharedStateHandle>(
get_backend_local_state(),
@@ -664,7 +664,7 @@ namespace snmalloc
SNMALLOC_ASSERT(!meta->is_unused());
check_client(
snmalloc_check_client(
Metaslab::is_start_of_object(
entry.get_sizeclass().as_small(), address_cast(p)),
"Not deallocating start of an object");

View File

@@ -181,7 +181,7 @@ namespace snmalloc
void check_prev(address_t signed_prev)
{
UNUSED(signed_prev);
check_client(
snmalloc_check_client(
signed_prev == this->prev_encoded,
"Heap corruption - free list corrupted!");
}

View File

@@ -523,7 +523,7 @@ namespace snmalloc
metaentry_chunk_sizeclass_to_slab_sizeclass(entry_sizeclass);
// Check for start of allocation.
check_client(
snmalloc_check_client(
pointer_align_down(p_tame, size) == p_tame,
"Not start of an allocation.");
@@ -560,7 +560,7 @@ namespace snmalloc
// If p_tame is not null, then dealloc has been call on something
// it shouldn't be called on.
// TODO: Should this be tested even in the !CHECK_CLIENT case?
check_client(p_tame == nullptr, "Not allocated by snmalloc.");
snmalloc_check_client(p_tame == nullptr, "Not allocated by snmalloc.");
# ifdef SNMALLOC_TRACING
std::cout << "nullptr deallocation" << std::endl;