From 1627de0e5f0acfef681570ce512c531d0d3a167f Mon Sep 17 00:00:00 2001 From: Dipin Hora Date: Mon, 3 Jul 2023 07:32:55 -0400 Subject: [PATCH] Add option to use pool implemenation which relies on memalign This commit add an option to build pony with the pool implementation based on posix_memalign instead of the default pool implementation. This, combined with address/undefined behavior/etc sanitizers, allows for easier debugging of memory issues within the compiler and runtime. This commit also includes two small bugfixes in the compiler that were discovered as part of this: * lexer.c fix in `normalise_string` to prevent issues when `lexer->buflen` == 0 * names.c fix in `names_typeparam` to prevent access to an AST node that was just replaced and a fix to the `Makefile` for running tests with `use=` flags and a fix to make sure the test runner gets rebuilt whenever ponyc gets rebuilt --- CMakeLists.txt | 5 ++ Makefile | 2 + src/libponyrt/CMakeLists.txt | 1 + src/libponyrt/mem/pool.c | 14 ++-- src/libponyrt/mem/pool.h | 6 ++ src/libponyrt/mem/pool_memalign.c | 130 ++++++++++++++++++++++++++++++ test/libponyrt/mem/pool.cc | 4 + 7 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 src/libponyrt/mem/pool_memalign.c diff --git a/CMakeLists.txt b/CMakeLists.txt index acd924bf95..a577270572 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,11 @@ if(PONY_USE_RUNTIMESTATS_MESSAGES) add_compile_options(-DUSE_RUNTIMESTATS -DUSE_RUNTIMESTATS_MESSAGES) endif() +if(PONY_USE_POOL_MEMALIGN) + set(PONY_OUTPUT_SUFFIX "-pool_memalign") + add_compile_options(-DUSE_POOL_MEMALIGN) +endif() + # LibPonyC tests assume that our outputs are two directories above the root directory. set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/../debug${PONY_OUTPUT_SUFFIX}") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/../release${PONY_OUTPUT_SUFFIX}") diff --git a/Makefile b/Makefile index 8cb305e935..cefa1a13fb 100644 --- a/Makefile +++ b/Makefile @@ -146,6 +146,8 @@ define USE_CHECK PONY_USES += -DPONY_USE_RUNTIMESTATS=true else ifeq ($1,runtimestats_messages) PONY_USES += -DPONY_USE_RUNTIMESTATS_MESSAGES=true + else ifeq ($1,pool_memalign) + PONY_USES += -DPONY_USE_POOL_MEMALIGN=true else $$(error ERROR: Unknown use option specified: $1) endif diff --git a/src/libponyrt/CMakeLists.txt b/src/libponyrt/CMakeLists.txt index 6f02eee701..4416f93d5a 100644 --- a/src/libponyrt/CMakeLists.txt +++ b/src/libponyrt/CMakeLists.txt @@ -39,6 +39,7 @@ set(_c_src mem/heap.c mem/pagemap.c mem/pool.c + mem/pool_memalign.c options/options.c platform/ponyassert.c platform/threads.c diff --git a/src/libponyrt/mem/pool.c b/src/libponyrt/mem/pool.c index 051a17c90d..d8181d1fc0 100644 --- a/src/libponyrt/mem/pool.c +++ b/src/libponyrt/mem/pool.c @@ -14,16 +14,18 @@ #include #include -#ifdef USE_VALGRIND -#include -#include -#endif - /// Allocations this size and above are aligned on this size. This is needed /// so that the pagemap for the heap is aligned. #define POOL_ALIGN_INDEX (POOL_ALIGN_BITS - POOL_MIN_BITS) #define POOL_ALIGN_MASK (POOL_ALIGN - 1) +#ifdef POOL_USE_DEFAULT + +#ifdef USE_VALGRIND +#include +#include +#endif + /// When we mmap, pull at least this many bytes. #ifdef PLATFORM_IS_ILP32 # define POOL_MMAP (16 * 1024 * 1024) // 16 MB @@ -998,6 +1000,8 @@ void ponyint_pool_thread_cleanup() pool_block_header.largest_size = 0; } +#endif + size_t ponyint_pool_index(size_t size) { #ifdef PLATFORM_IS_ILP32 diff --git a/src/libponyrt/mem/pool.h b/src/libponyrt/mem/pool.h index 86614fa341..02bcc44260 100644 --- a/src/libponyrt/mem/pool.h +++ b/src/libponyrt/mem/pool.h @@ -6,6 +6,12 @@ #include +#if defined(USE_POOL_MEMALIGN) +# define POOL_USE_MEMALIGN +#else +# define POOL_USE_DEFAULT +#endif + PONY_EXTERN_C_BEGIN /* Because of the way free memory is reused as its own linked list container, diff --git a/src/libponyrt/mem/pool_memalign.c b/src/libponyrt/mem/pool_memalign.c new file mode 100644 index 0000000000..cebfe861e3 --- /dev/null +++ b/src/libponyrt/mem/pool_memalign.c @@ -0,0 +1,130 @@ +#define PONY_WANT_ATOMIC_DEFS + +#include "pool.h" +#include "alloc.h" +#include "../ds/fun.h" +#include "../sched/cpu.h" +#include "ponyassert.h" +#include +#include +#include +#include +#include + +#include +#include + +#ifdef POOL_USE_MEMALIGN + +void* ponyint_pool_alloc(size_t index) +{ + void* p; + size_t size = POOL_SIZE(index); + if(size < POOL_ALIGN) + { + p = malloc(size); + } + else + { + int code = posix_memalign(&p, POOL_ALIGN, size); + pony_assert(code == 0); + } + + return p; +} + +void ponyint_pool_free(size_t index, void* p) +{ + pony_assert(index < POOL_COUNT); + + free(p); +} + +static void* pool_alloc_size(size_t size) +{ + void* p; + int code = posix_memalign(&p, POOL_ALIGN, size); + pony_assert(code == 0); + + return p; +} + +void* ponyint_pool_alloc_size(size_t size) +{ + size_t index = ponyint_pool_index(size); + + if(index < POOL_COUNT) + return ponyint_pool_alloc(index); + + size = ponyint_pool_adjust_size(size); + void* p = pool_alloc_size(size); + + return p; +} + +static void pool_free_size(size_t size, void* p) +{ + free(p); + (void)size; +} + +void ponyint_pool_free_size(size_t size, void* p) +{ + size_t index = ponyint_pool_index(size); + + if(index < POOL_COUNT) + return ponyint_pool_free(index, p); + + size = ponyint_pool_adjust_size(size); + pool_free_size(size, p); +} + +void* ponyint_pool_realloc_size(size_t old_size, size_t new_size, void* p) +{ + // Can only reuse the old pointer if the old index/adjusted size is equal to + // the new one, not greater. + + if(p == NULL) + return ponyint_pool_alloc_size(new_size); + + size_t old_index = ponyint_pool_index(old_size); + size_t new_index = ponyint_pool_index(new_size); + size_t old_adj_size = 0; + + void* new_p; + + if(new_index < POOL_COUNT) + { + if(old_index == new_index) + return p; + + new_p = ponyint_pool_alloc(new_index); + } else { + size_t new_adj_size = ponyint_pool_adjust_size(new_size); + + if(old_index >= POOL_COUNT) + { + old_adj_size = ponyint_pool_adjust_size(old_size); + + if(old_adj_size == new_adj_size) + return p; + } + + new_p = pool_alloc_size(new_adj_size); + } + + memcpy(new_p, p, old_size < new_size ? old_size : new_size); + + if(old_index < POOL_COUNT) + ponyint_pool_free(old_index, p); + else + pool_free_size(old_adj_size, p); + + return new_p; +} + +void ponyint_pool_thread_cleanup() +{ +} + +#endif diff --git a/test/libponyrt/mem/pool.cc b/test/libponyrt/mem/pool.cc index 1e2c358423..b386ff7d47 100644 --- a/test/libponyrt/mem/pool.cc +++ b/test/libponyrt/mem/pool.cc @@ -4,6 +4,8 @@ #include +#ifdef POOL_USE_DEFAULT + typedef char block_t[32]; TEST(Pool, Fifo) @@ -71,6 +73,8 @@ TEST(Pool, LargeAlloc) ponyint_pool_free_size(1 << 20, p); } +#endif + TEST(Pool, Index) { size_t index = ponyint_pool_index(1);