From 276b0362bf940838bef6da621628cfa8cf0a7cc4 Mon Sep 17 00:00:00 2001 From: Stanislav Paskalev Date: Tue, 28 Dec 2021 11:35:24 +0200 Subject: [PATCH 1/5] Replace `_Bool` with `unsigned int` to facilitate interop with cpp compilers that don't have _Bool --- buddy_alloc.h | 54 ++++++++++++++++----------------- tests.c | 84 +++++++++++++++++++++++++-------------------------- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/buddy_alloc.h b/buddy_alloc.h index 88b4f47..766cf91 100644 --- a/buddy_alloc.h +++ b/buddy_alloc.h @@ -41,10 +41,10 @@ struct buddy *buddy_embed(unsigned char *main, size_t memory_size); struct buddy *buddy_resize(struct buddy *buddy, size_t new_memory_size); /* Tests if the allocation can be shrunk in half */ -_Bool buddy_can_shrink(struct buddy *buddy); +unsigned int buddy_can_shrink(struct buddy *buddy); /* Tests if the allocation is completely empty */ -_Bool buddy_is_empty(struct buddy *buddy); +unsigned int buddy_is_empty(struct buddy *buddy); /* Reports the arena size */ size_t buddy_arena_size(struct buddy *buddy); @@ -105,7 +105,7 @@ static size_t buddy_tree_sizeof(uint8_t order); static struct buddy_tree *buddy_tree_init(unsigned char *at, uint8_t order); /* Indicates whether this is a valid position for the tree */ -static _Bool buddy_tree_valid(struct buddy_tree *t, buddy_tree_pos pos); +static unsigned int buddy_tree_valid(struct buddy_tree *t, buddy_tree_pos pos); /* Returns the order of the specified buddy allocation tree */ static uint8_t buddy_tree_order(struct buddy_tree *t); @@ -146,7 +146,7 @@ static size_t buddy_tree_index(buddy_tree_pos pos); static struct buddy_tree_interval buddy_tree_interval(struct buddy_tree *t, buddy_tree_pos pos); /* Checks if one interval contains another */ -static _Bool buddy_tree_interval_contains(struct buddy_tree_interval outer, +static unsigned int buddy_tree_interval_contains(struct buddy_tree_interval outer, struct buddy_tree_interval inner); /* @@ -166,10 +166,10 @@ static void buddy_tree_release(struct buddy_tree *t, buddy_tree_pos pos); static buddy_tree_pos buddy_tree_find_free(struct buddy_tree *t, uint8_t depth); /* Tests if the incidated position is available for allocation */ -static _Bool buddy_tree_is_free(struct buddy_tree *t, buddy_tree_pos pos); +static unsigned int buddy_tree_is_free(struct buddy_tree *t, buddy_tree_pos pos); /* Tests if the tree can be shrank in half */ -static _Bool buddy_tree_can_shrink(struct buddy_tree *t); +static unsigned int buddy_tree_can_shrink(struct buddy_tree *t); /* * Debug functions @@ -179,7 +179,7 @@ static _Bool buddy_tree_can_shrink(struct buddy_tree *t); static void buddy_tree_debug(FILE *stream, struct buddy_tree *t, buddy_tree_pos pos, size_t start_size); /* Implementation defined */ -static _Bool buddy_tree_check_invariant(struct buddy_tree *t, buddy_tree_pos pos); +static unsigned int buddy_tree_check_invariant(struct buddy_tree *t, buddy_tree_pos pos); /* * A char-backed bitset implementation @@ -197,7 +197,7 @@ static inline void bitset_set(unsigned char *bitset, size_t pos); static inline void bitset_clear(unsigned char *bitset, size_t pos); -static inline _Bool bitset_test(const unsigned char *bitset, size_t pos); +static inline unsigned int bitset_test(const unsigned char *bitset, size_t pos); static void bitset_shift_left(unsigned char *bitset, size_t from_pos, size_t to_pos, size_t by); @@ -235,12 +235,12 @@ struct buddy { unsigned char *main; ptrdiff_t main_offset; }; - _Bool relative_mode; + unsigned int relative_mode; unsigned char buddy_tree[]; }; struct buddy_embed_check { - _Bool can_fit; + unsigned int can_fit; size_t offset; size_t buddy_size; }; @@ -252,10 +252,10 @@ static void *address_for_position(struct buddy *buddy, buddy_tree_pos pos); static buddy_tree_pos position_for_address(struct buddy *buddy, const unsigned char *addr); static unsigned char *buddy_main(struct buddy *buddy); static struct buddy_tree *buddy_tree(struct buddy *buddy); -static void buddy_toggle_virtual_slots(struct buddy *buddy, _Bool state); +static void buddy_toggle_virtual_slots(struct buddy *buddy, unsigned int state); static struct buddy *buddy_resize_standard(struct buddy *buddy, size_t new_memory_size); static struct buddy *buddy_resize_embedded(struct buddy *buddy, size_t new_memory_size); -static _Bool buddy_is_free(struct buddy *buddy, size_t from); +static unsigned int buddy_is_free(struct buddy *buddy, size_t from); static struct buddy_embed_check buddy_embed_offset(size_t memory_size); static buddy_tree_pos deepest_position_for_offset(struct buddy *buddy, size_t offset); @@ -383,14 +383,14 @@ static struct buddy *buddy_resize_embedded(struct buddy *buddy, size_t new_memor return relocated; } -_Bool buddy_can_shrink(struct buddy *buddy) { +unsigned int buddy_can_shrink(struct buddy *buddy) { if (buddy == NULL) { return 0; } return buddy_is_free(buddy, buddy->memory_size / 2); } -_Bool buddy_is_empty(struct buddy *buddy) { +unsigned int buddy_is_empty(struct buddy *buddy) { if (buddy == NULL) { return 1; } @@ -612,7 +612,7 @@ static unsigned char *buddy_main(struct buddy *buddy) { return buddy->main; } -static void buddy_toggle_virtual_slots(struct buddy *buddy, _Bool state) { +static void buddy_toggle_virtual_slots(struct buddy *buddy, unsigned int state) { size_t memory_size = buddy->memory_size; /* Mask/unmask the virtual space if memory is not a power of two */ size_t effective_memory_size = ceiling_power_of_two(memory_size); @@ -661,7 +661,7 @@ static void buddy_toggle_virtual_slots(struct buddy *buddy, _Bool state) { /* Internal function that checks if there are any allocations after the indicated relative memory index. Used to check if the arena can be downsized. */ -static _Bool buddy_is_free(struct buddy *buddy, size_t from) { +static unsigned int buddy_is_free(struct buddy *buddy, size_t from) { /* from is already adjusted for alignment */ size_t effective_memory_size = ceiling_power_of_two(buddy->memory_size); @@ -908,7 +908,7 @@ static void buddy_tree_shrink(struct buddy_tree *t, uint8_t desired_order) { } } -static _Bool buddy_tree_valid(struct buddy_tree *t, buddy_tree_pos pos) { +static unsigned int buddy_tree_valid(struct buddy_tree *t, buddy_tree_pos pos) { return pos && (pos < t->upper_pos_bound); } @@ -1020,7 +1020,7 @@ static struct buddy_tree_interval buddy_tree_interval(struct buddy_tree *t, budd return result; } -static _Bool buddy_tree_interval_contains(struct buddy_tree_interval outer, +static unsigned int buddy_tree_interval_contains(struct buddy_tree_interval outer, struct buddy_tree_interval inner) { return (inner.from >= outer.from) && (inner.from <= outer.to) @@ -1139,7 +1139,7 @@ static buddy_tree_pos buddy_tree_find_free(struct buddy_tree *t, uint8_t target_ } } -static _Bool buddy_tree_is_free(struct buddy_tree *t, buddy_tree_pos pos) { +static unsigned int buddy_tree_is_free(struct buddy_tree *t, buddy_tree_pos pos) { if (buddy_tree_status(t, pos)) { return 0; } @@ -1155,7 +1155,7 @@ static _Bool buddy_tree_is_free(struct buddy_tree *t, buddy_tree_pos pos) { return 1; } -static _Bool buddy_tree_can_shrink(struct buddy_tree *t) { +static unsigned int buddy_tree_can_shrink(struct buddy_tree *t) { if (buddy_tree_status(t, buddy_tree_right_child(buddy_tree_root())) != 0) { return 0; /* Refusing to shrink with right subtree still used! */ } @@ -1175,7 +1175,7 @@ static void buddy_tree_debug(FILE *stream, struct buddy_tree *t, buddy_tree_pos } buddy_tree_pos start = pos; - _Bool going_up = 0; + unsigned int going_up = 0; while (1) { if (going_up) { if (pos == start) { @@ -1215,10 +1215,10 @@ static void buddy_tree_debug(FILE *stream, struct buddy_tree *t, buddy_tree_pos fflush(stdout); } -static _Bool buddy_tree_check_invariant(struct buddy_tree *t, buddy_tree_pos pos) { +static unsigned int buddy_tree_check_invariant(struct buddy_tree *t, buddy_tree_pos pos) { buddy_tree_pos start = pos; - _Bool going_up = 0; - _Bool fail = 0; + unsigned int going_up = 0; + unsigned int fail = 0; while (1) { if (going_up) { if (pos == start) { @@ -1239,7 +1239,7 @@ static _Bool buddy_tree_check_invariant(struct buddy_tree *t, buddy_tree_pos pos size_t left_child_status = buddy_tree_status(t, buddy_tree_left_child(pos)); size_t right_child_status = buddy_tree_status(t, buddy_tree_right_child(pos)); - _Bool violated = 0; + unsigned int violated = 0; if (left_child_status || right_child_status) { size_t min = left_child_status <= right_child_status @@ -1291,7 +1291,7 @@ static inline void bitset_clear(unsigned char *bitset, size_t pos) { bitset[bucket] &= ~bitset_index_mask[index]; } -static inline _Bool bitset_test(const unsigned char *bitset, size_t pos) { +static inline unsigned int bitset_test(const unsigned char *bitset, size_t pos) { size_t bucket = pos / CHAR_BIT; size_t index = pos % CHAR_BIT; return bitset[bucket] & bitset_index_mask[index]; @@ -1393,7 +1393,7 @@ static void bitset_shift_right(unsigned char *bitset, size_t from_pos, size_t to static void bitset_debug(FILE *stream, unsigned char *bitset, size_t length) { for (size_t i = 0; i < length; i++) { - fprintf(stream, "%zu: %d\n", i, bitset_test(bitset, i)); + fprintf(stream, "%zu: %d\n", i, bitset_test(bitset, i) && 1); } } diff --git a/tests.c b/tests.c index 7af0548..7fa2215 100644 --- a/tests.c +++ b/tests.c @@ -44,9 +44,9 @@ void test_bitset_basic() { assert(bitset_sizeof(7) == 1); assert(bitset_sizeof(8) == 1); assert(bitset_sizeof(9) == 2); - assert(bitset_test(buf, 0) == 0); + assert(!bitset_test(buf, 0)); bitset_set(buf, 0); - assert(bitset_test(buf, 0) == 1); + assert(bitset_test(buf, 0)); } void test_bitset_range() { @@ -86,56 +86,56 @@ void test_bitset_shift() { bitset_set(buf, 4); bitset_set(buf, 7); bitset_shift_right(buf, 0, 8, 4); - assert(bitset_test(buf, 0) == 0); - assert(bitset_test(buf, 1) == 0); - assert(bitset_test(buf, 2) == 0); - assert(bitset_test(buf, 3) == 0); - assert(bitset_test(buf, 4) == 1); - assert(bitset_test(buf, 5) == 0); - assert(bitset_test(buf, 6) == 0); - assert(bitset_test(buf, 7) == 1); - assert(bitset_test(buf, 8) == 1); - assert(bitset_test(buf, 9) == 0); - assert(bitset_test(buf, 10) == 0); - assert(bitset_test(buf, 11) == 1); - assert(bitset_test(buf, 12) == 0); - assert(bitset_test(buf, 13) == 0); - assert(bitset_test(buf, 14) == 0); - assert(bitset_test(buf, 15) == 0); + assert(!bitset_test(buf, 0)); + assert(!bitset_test(buf, 1)); + assert(!bitset_test(buf, 2)); + assert(!bitset_test(buf, 3)); + assert(bitset_test(buf, 4)); + assert(!bitset_test(buf, 5)); + assert(!bitset_test(buf, 6)); + assert(bitset_test(buf, 7)); + assert(bitset_test(buf, 8)); + assert(!bitset_test(buf, 9)); + assert(!bitset_test(buf, 10)); + assert(bitset_test(buf, 11)); + assert(!bitset_test(buf, 12)); + assert(!bitset_test(buf, 13)); + assert(!bitset_test(buf, 14)); + assert(!bitset_test(buf, 15)); bitset_shift_left(buf, 4, 12, 4); - assert(bitset_test(buf, 0) == 1); - assert(bitset_test(buf, 1) == 0); - assert(bitset_test(buf, 2) == 0); - assert(bitset_test(buf, 3) == 1); - assert(bitset_test(buf, 4) == 1); - assert(bitset_test(buf, 5) == 0); - assert(bitset_test(buf, 6) == 0); - assert(bitset_test(buf, 7) == 1); - assert(bitset_test(buf, 8) == 0); - assert(bitset_test(buf, 9) == 0); - assert(bitset_test(buf, 10) == 0); - assert(bitset_test(buf, 11) == 0); - assert(bitset_test(buf, 12) == 0); - assert(bitset_test(buf, 13) == 0); - assert(bitset_test(buf, 14) == 0); - assert(bitset_test(buf, 15) == 0); + assert(bitset_test(buf, 0)); + assert(!bitset_test(buf, 1)); + assert(!bitset_test(buf, 2)); + assert(bitset_test(buf, 3)); + assert(bitset_test(buf, 4)); + assert(!bitset_test(buf, 5)); + assert(!bitset_test(buf, 6)); + assert(bitset_test(buf, 7)); + assert(!bitset_test(buf, 8)); + assert(!bitset_test(buf, 9)); + assert(!bitset_test(buf, 10)); + assert(!bitset_test(buf, 11)); + assert(!bitset_test(buf, 12)); + assert(!bitset_test(buf, 13)); + assert(!bitset_test(buf, 14)); + assert(!bitset_test(buf, 15)); } void test_bitset_shift_invalid() { start_test; unsigned char buf[4096] = {0}; bitset_set_range(buf, 1, 0); /* no-op */ - assert(bitset_test(buf, 0) == 0); - assert(bitset_test(buf, 1) == 0); + assert(!bitset_test(buf, 0)); + assert(!bitset_test(buf, 1)); bitset_set_range(buf, 0, 1); - assert(bitset_test(buf, 0) == 1); - assert(bitset_test(buf, 1) == 1); + assert(bitset_test(buf, 0)); + assert(bitset_test(buf, 1)); bitset_clear_range(buf, 1, 0) /* no-op */; - assert(bitset_test(buf, 0) == 1); - assert(bitset_test(buf, 1) == 1); + assert(bitset_test(buf, 0)); + assert(bitset_test(buf, 1)); bitset_clear_range(buf, 0, 1); - assert(bitset_test(buf, 0) == 0); - assert(bitset_test(buf, 1) == 0); + assert(!bitset_test(buf, 0)); + assert(!bitset_test(buf, 1)); } void test_bitset_debug() { From 060139cc739faf9655ff9f4903c5f0150e656afc Mon Sep 17 00:00:00 2001 From: Stanislav Paskalev Date: Tue, 28 Dec 2021 14:28:35 +0200 Subject: [PATCH 2/5] Replace __builtin_popcount with a lookup table implementation, added a test --- buddy_alloc.h | 19 ++++++++++++++----- tests.c | 7 +++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/buddy_alloc.h b/buddy_alloc.h index 766cf91..930fd48 100644 --- a/buddy_alloc.h +++ b/buddy_alloc.h @@ -214,6 +214,9 @@ static void bitset_debug(FILE *stream, unsigned char *bitset, size_t length); Bits */ +/* Returns the number of set bits in the given byte */ +static unsigned int popcount_byte(unsigned char b); + /* Returns the index of the highest bit set (1-based) */ static inline size_t highest_bit_position(size_t value); @@ -1297,7 +1300,7 @@ static inline unsigned int bitset_test(const unsigned char *bitset, size_t pos) return bitset[bucket] & bitset_index_mask[index]; } -static uint8_t bitset_char_mask[8][8] = { +static const uint8_t bitset_char_mask[8][8] = { {1, 3, 7, 15, 31, 63, 127, 255}, {0, 2, 6, 14, 30, 62, 126, 254}, {0, 0, 4, 12, 28, 60, 124, 252}, @@ -1352,13 +1355,13 @@ static size_t bitset_count_range(unsigned char *bitset, size_t from_pos, size_t size_t to_index = to_pos % CHAR_BIT; if (from_bucket == to_bucket) { - return __builtin_popcount(bitset[from_bucket] & bitset_char_mask[from_index][to_index]); + return popcount_byte(bitset[from_bucket] & bitset_char_mask[from_index][to_index]); } - size_t result = __builtin_popcount(bitset[from_bucket] & bitset_char_mask[from_index][7]) - + __builtin_popcount(bitset[to_bucket] & bitset_char_mask[0][to_index]); + size_t result = popcount_byte(bitset[from_bucket] & bitset_char_mask[from_index][7]) + + popcount_byte(bitset[to_bucket] & bitset_char_mask[0][to_index]); while(++from_bucket != to_bucket) { - result += __builtin_popcount(bitset[from_bucket]); + result += popcount_byte(bitset[from_bucket]); } return result; } @@ -1401,6 +1404,12 @@ static void bitset_debug(FILE *stream, unsigned char *bitset, size_t length) { Bits */ +static const unsigned char popcount_lookup[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; + +static unsigned int popcount_byte(unsigned char b) { + return popcount_lookup[b & 15] + popcount_lookup[b >> 4]; +} + /* Returns the higest set bit position for the given value. Do not call with zero. */ static inline size_t highest_bit_position(size_t value) { assert(value); diff --git a/tests.c b/tests.c index 7fa2215..6d2cbe7 100644 --- a/tests.c +++ b/tests.c @@ -38,6 +38,12 @@ void test_ceiling_power_of_two() { assert(ceiling_power_of_two(8) == 8); } +void test_popcount_byte() { + for (size_t i = 0; i < 256; i++) { + assert(popcount_byte(i) == (popcount_byte(i / 2) + (i & 1))); + } +} + void test_bitset_basic() { start_test; unsigned char buf[4] = {0}; @@ -1336,6 +1342,7 @@ int main() { { test_highest_bit_position(); test_ceiling_power_of_two(); + test_popcount_byte(); } { From 6fa04779acbce879e4bedcdf02c87e450fe08103 Mon Sep 17 00:00:00 2001 From: Stanislav Paskalev Date: Tue, 28 Dec 2021 16:23:13 +0200 Subject: [PATCH 3/5] Replace __builtin_clzl with a binary-search-like highest_bit_position implementation --- buddy_alloc.h | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/buddy_alloc.h b/buddy_alloc.h index 930fd48..f6da7a9 100644 --- a/buddy_alloc.h +++ b/buddy_alloc.h @@ -218,7 +218,7 @@ static void bitset_debug(FILE *stream, unsigned char *bitset, size_t length); static unsigned int popcount_byte(unsigned char b); /* Returns the index of the highest bit set (1-based) */ -static inline size_t highest_bit_position(size_t value); +static size_t highest_bit_position(size_t value); /* Returns the nearest larger or equal power of two */ static inline size_t ceiling_power_of_two(size_t value); @@ -1410,15 +1410,23 @@ static unsigned int popcount_byte(unsigned char b) { return popcount_lookup[b & 15] + popcount_lookup[b >> 4]; } -/* Returns the higest set bit position for the given value. Do not call with zero. */ -static inline size_t highest_bit_position(size_t value) { - assert(value); - return ((sizeof(size_t) * CHAR_BIT) - __builtin_clzl(value)); +/* Returns the higest set bit position for the given value. Returns zero for zero. */ +static size_t highest_bit_position(size_t value) { + int result = 0; + const size_t all_set[] = {4294967295, 65535, 255, 15, 7, 3, 1}; + const size_t count[] = {32, 16, 8, 4, 2, 1, 1}; + for (size_t i = 0; i < 7; i++) { + if (value >= all_set[i]) { + value >>= count[i]; + result += count[i]; + } + } + return result + value; } static inline size_t ceiling_power_of_two(size_t value) { value += !value; /* branchless x -> { 1 for 0, x for x } */ - return 1u << ((sizeof(size_t) * CHAR_BIT) - __builtin_clzl(value + value - 1)-1); + return 1u << (highest_bit_position(value + value - 1)-1); } #endif /* BUDDY_ALLOC_IMPLEMENTATION */ From eb2a9fb8bc0308b671eb167c5ad831aa429255af Mon Sep 17 00:00:00 2001 From: Stanislav Paskalev Date: Tue, 28 Dec 2021 16:27:21 +0200 Subject: [PATCH 4/5] Add an explicit (unsigned char *) cast before calling position_for_address in realloc --- buddy_alloc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buddy_alloc.h b/buddy_alloc.h index f6da7a9..be13621 100644 --- a/buddy_alloc.h +++ b/buddy_alloc.h @@ -485,7 +485,7 @@ void *buddy_realloc(struct buddy *buddy, void *ptr, size_t requested_size) { } /* Find the position tracking this address */ - buddy_tree_pos origin = position_for_address(buddy, ptr); + buddy_tree_pos origin = position_for_address(buddy, (unsigned char *) ptr); if (!origin) { return NULL; } From 8298efd206c54df4bd95d4b244cb1479bb0ae85e Mon Sep 17 00:00:00 2001 From: Stanislav Paskalev Date: Tue, 28 Dec 2021 17:15:47 +0200 Subject: [PATCH 5/5] Do not import --- buddy_alloc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/buddy_alloc.h b/buddy_alloc.h index be13621..e75d88b 100644 --- a/buddy_alloc.h +++ b/buddy_alloc.h @@ -16,7 +16,6 @@ #define BUDDY_ALLOC_H #include -#include #include #include #include