Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add yp_alloc to begin creating our own allocator #1375

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/yarp/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "yarp/util/yp_list.h"
#include "yarp/util/yp_newline_list.h"
#include "yarp/util/yp_state_stack.h"
#include "yarp/util/yp_alloc.h"

#include <stdbool.h>

Expand Down Expand Up @@ -316,6 +317,8 @@ struct yp_parser {
size_t index; // the current index into the lexer mode stack
} lex_modes;

yp_allocator_t allocator;

const uint8_t *start; // the pointer to the start of the source
const uint8_t *end; // the pointer to the end of the source
yp_token_t previous; // the previous token we were considering
Expand Down
38 changes: 38 additions & 0 deletions include/yarp/util/yp_alloc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#ifndef YARP_ALLOC_H
#define YARP_ALLOC_H

#include <stdbool.h>
#include <stdlib.h>

typedef struct yp_memory_pool {
size_t capacity;
size_t size;
char *memory;
struct yp_memory_pool *prev;
} yp_memory_pool_t;

typedef struct yp_allocator {
size_t capacity;
size_t size;
size_t pool_count;
yp_memory_pool_t *pool;

yp_memory_pool_t *large_pool;
} yp_allocator_t;

void *
yp_malloc(yp_allocator_t *allocator, size_t size);

void *
yp_calloc(yp_allocator_t *allocator, size_t num, size_t size);

void
yp_free(yp_allocator_t *allocator, void *ptr);

void
yp_allocator_init(yp_allocator_t *allocator, size_t size);

void
yp_allocator_free(yp_allocator_t *allocator);

#endif // YARP_ALLOC_H
146 changes: 146 additions & 0 deletions src/util/yp_alloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#include <assert.h>
#include "yarp/defines.h"
#include "yarp/util/yp_alloc.h"

#define DEFAULT_POOL_SIZE 16384

// Create a section of memory we can use instead of mallocing later.
static yp_memory_pool_t *
yp_memory_pool_init(size_t capacity) {
yp_memory_pool_t * pool = malloc(sizeof(yp_memory_pool_t));
pool->capacity = capacity;
pool->size = 0;
pool->prev = NULL;
pool->memory = malloc(capacity);
return pool;
}

// allocate a new pool if we don't have enough available space for the next allocation.
static void
yp_ensure_available_memory(yp_allocator_t *allocator, size_t size) {
if (allocator->pool->size + size >= allocator->pool->capacity) {
allocator->pool_count++;

yp_memory_pool_t *prev_pool = allocator->pool;
allocator->pool = yp_memory_pool_init(DEFAULT_POOL_SIZE);
allocator->pool->prev = prev_pool;
}
}

// Internal malloc used in place of malloc().
// It acts as a simple sbrk style allocator where
// each allocation is directly aftr the last until
// the memory pool is full.
void *
yp_malloc(yp_allocator_t *allocator, size_t size) {
size = (size + 7) & ~7UL;

if (size >= DEFAULT_POOL_SIZE) {
yp_memory_pool_t *large_pool = yp_memory_pool_init(size + 1);
if (allocator->large_pool == NULL) {
allocator->large_pool = large_pool;
} else {
yp_memory_pool_t *prev_pool = allocator->large_pool;
allocator->large_pool = large_pool;
allocator->large_pool->prev = prev_pool;
}
return large_pool->memory;
} else {
yp_ensure_available_memory(allocator, size);

void *ptr = allocator->pool->memory + allocator->pool->size;
allocator->pool->size += size;

return ptr;
}
}

// Internal calloc used in place of calloc().
// has the same behavior as yp_malloc, but
// returns the memory zeroed.
void *
yp_calloc(yp_allocator_t *allocator, size_t num, size_t size) {
if (num * size < size) {
return NULL;
}
void *ptr = yp_malloc(allocator, num * size);
memset(ptr, 0, num * size);
return ptr;
}

// Internal free used in place of free().
// Since YARP does very few free calls,
// we treat free as a no-op and leave
// the memory that is not accessed in place
// until the whole allocator is freed.
//
// Additionally with YARP_DEBUG_MODE_BUILD
// enabled, yp_free checks that the freed memory
// is within one of the memory pools.
void
yp_free(yp_allocator_t *allocator, void *ptr) {
#ifdef YARP_DEBUG_MODE_BUILD
bool found = false;
yp_memory_pool_t *pool = allocator->pool;

while (pool != NULL) {
yp_memory_pool_t *prev = pool->prev;
if (found == false && (char *)ptr >= pool->memory && (char *)ptr < pool->memory + pool->size) {
found = true;
}
pool = prev;
}

pool = allocator->large_pool;

while (pool != NULL) {
yp_memory_pool_t *prev = pool->prev;
if (found == false && (char *)ptr >= pool->memory && (char *)ptr < pool->memory + pool->size) {
found = true;
}
pool = prev;
}
assert(found);
#else
(void)allocator;
(void)ptr;
#endif // YARP_DEBUG_MODE_BUILD
}

// create a new allocator with default pool size.
//
// TODO: when we have a good guess at the memory
// useage's relationship to file size, this
// can use that information to make the first
// pool likely to be the only pool.
void
yp_allocator_init(yp_allocator_t *allocator, size_t capacity) {
(void)capacity;
allocator->pool_count = 0;
allocator->pool = yp_memory_pool_init(DEFAULT_POOL_SIZE);
allocator->large_pool = NULL;
}

// free the allocator and all associated pools.
// Called each time we finish parsing a file.
void
yp_allocator_free(yp_allocator_t *allocator) {
(void)allocator;
yp_memory_pool_t *pool = allocator->pool;

while (pool != NULL) {
yp_memory_pool_t *prev = pool->prev;
free(pool->memory);
free(pool);
pool = prev;
}

pool = allocator->large_pool;

while (pool != NULL) {
yp_memory_pool_t *prev = pool->prev;
free(pool->memory);
free(pool);
pool = prev;
}
}
39 changes: 21 additions & 18 deletions src/yarp.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ lex_mode_push(yp_parser_t *parser, yp_lex_mode_t lex_mode) {
parser->lex_modes.index++;

if (parser->lex_modes.index > YP_LEX_STACK_SIZE - 1) {
parser->lex_modes.current = (yp_lex_mode_t *) malloc(sizeof(yp_lex_mode_t));
parser->lex_modes.current = (yp_lex_mode_t *) yp_malloc(&parser->allocator, sizeof(yp_lex_mode_t));
if (parser->lex_modes.current == NULL) return false;

*parser->lex_modes.current = lex_mode;
Expand Down Expand Up @@ -342,7 +342,7 @@ lex_mode_pop(yp_parser_t *parser) {
} else {
parser->lex_modes.index--;
yp_lex_mode_t *prev = parser->lex_modes.current->prev;
free(parser->lex_modes.current);
yp_free(&parser->allocator, parser->lex_modes.current);
parser->lex_modes.current = prev;
}
}
Expand Down Expand Up @@ -622,7 +622,7 @@ parse_decimal_number(yp_parser_t *parser, const uint8_t *start, const uint8_t *e
assert(diff > 0 && ((unsigned long) diff < SIZE_MAX));
size_t length = (size_t) diff;

char *digits = calloc(length + 1, sizeof(char));
char *digits = yp_calloc(&parser->allocator, length + 1, sizeof(char));
memcpy(digits, start, length);
digits[length] = '\0';

Expand All @@ -635,7 +635,7 @@ parse_decimal_number(yp_parser_t *parser, const uint8_t *start, const uint8_t *e
value = UINT32_MAX;
}

free(digits);
yp_free(&parser->allocator, digits);

if (value > UINT32_MAX) {
yp_diagnostic_list_append(&parser->error_list, start, end, "invalid decimal number");
Expand Down Expand Up @@ -681,7 +681,7 @@ yp_statements_node_body_append(yp_statements_node_t *node, yp_node_t *statement)
// implement our own arena allocation.
static inline void *
yp_alloc_node(YP_ATTRIBUTE_UNUSED yp_parser_t *parser, size_t size) {
void *memory = calloc(1, size);
void *memory = yp_calloc(&parser->allocator, 1, size);
if (memory == NULL) {
fprintf(stderr, "Failed to allocate %zu bytes\n", size);
abort();
Expand Down Expand Up @@ -1495,7 +1495,7 @@ yp_call_and_write_node_create(yp_parser_t *parser, yp_call_node_t *target, const
// Here we're going to free the target, since it is no longer necessary.
// However, we don't want to call `yp_node_destroy` because we want to keep
// around all of its children since we just reused them.
free(target);
yp_free(&parser->allocator, target);

return node;
}
Expand Down Expand Up @@ -1533,7 +1533,7 @@ yp_call_operator_write_node_create(yp_parser_t *parser, yp_call_node_t *target,
// Here we're going to free the target, since it is no longer necessary.
// However, we don't want to call `yp_node_destroy` because we want to keep
// around all of its children since we just reused them.
free(target);
yp_free(&parser->allocator, target);

return node;
}
Expand Down Expand Up @@ -1571,7 +1571,7 @@ yp_call_or_write_node_create(yp_parser_t *parser, yp_call_node_t *target, const
// Here we're going to free the target, since it is no longer necessary.
// However, we don't want to call `yp_node_destroy` because we want to keep
// around all of its children since we just reused them.
free(target);
yp_free(&parser->allocator, target);

return node;
}
Expand Down Expand Up @@ -4232,7 +4232,7 @@ yp_string_node_to_symbol_node(yp_parser_t *parser, yp_string_node_t *node, const
// We are explicitly _not_ using yp_node_destroy here because we don't want
// to trash the unescaped string. We could instead copy the string if we
// know that it is owned, but we're taking the fast path for now.
free(node);
yp_free(&parser->allocator, node);

return new_node;
}
Expand All @@ -4256,7 +4256,7 @@ yp_symbol_node_to_string_node(yp_parser_t *parser, yp_symbol_node_t *node) {
// We are explicitly _not_ using yp_node_destroy here because we don't want
// to trash the unescaped string. We could instead copy the string if we
// know that it is owned, but we're taking the fast path for now.
free(node);
yp_free(&parser->allocator, node);

return new_node;
}
Expand Down Expand Up @@ -4563,7 +4563,7 @@ yp_yield_node_create(yp_parser_t *parser, const yp_token_t *keyword, const yp_lo
// Allocate and initialize a new scope. Push it onto the scope stack.
static bool
yp_parser_scope_push(yp_parser_t *parser, bool closed) {
yp_scope_t *scope = (yp_scope_t *) malloc(sizeof(yp_scope_t));
yp_scope_t *scope = (yp_scope_t *) yp_malloc(&parser->allocator, sizeof(yp_scope_t));
if (scope == NULL) return false;

*scope = (yp_scope_t) { .closed = closed, .previous = parser->current_scope };
Expand Down Expand Up @@ -4630,7 +4630,7 @@ static void
yp_parser_scope_pop(yp_parser_t *parser) {
yp_scope_t *scope = parser->current_scope;
parser->current_scope = scope->previous;
free(scope);
yp_free(&parser->allocator, scope);
}

/******************************************************************************/
Expand Down Expand Up @@ -5036,7 +5036,7 @@ context_recoverable(yp_parser_t *parser, yp_token_t *token) {

static bool
context_push(yp_parser_t *parser, yp_context_t context) {
yp_context_node_t *context_node = (yp_context_node_t *) malloc(sizeof(yp_context_node_t));
yp_context_node_t *context_node = (yp_context_node_t *) yp_malloc(&parser->allocator, sizeof(yp_context_node_t));
if (context_node == NULL) return false;

*context_node = (yp_context_node_t) { .context = context, .prev = NULL };
Expand All @@ -5054,7 +5054,7 @@ context_push(yp_parser_t *parser, yp_context_t context) {
static void
context_pop(yp_parser_t *parser) {
yp_context_node_t *prev = parser->current_context->prev;
free(parser->current_context);
yp_free(&parser->allocator, parser->current_context);
parser->current_context = prev;
}

Expand Down Expand Up @@ -5762,7 +5762,7 @@ parser_lex_callback(yp_parser_t *parser) {
// Return a new comment node of the specified type.
static inline yp_comment_t *
parser_comment(yp_parser_t *parser, yp_comment_type_t type) {
yp_comment_t *comment = (yp_comment_t *) malloc(sizeof(yp_comment_t));
yp_comment_t *comment = (yp_comment_t *) yp_malloc(&parser->allocator, sizeof(yp_comment_t));
if (comment == NULL) return NULL;

*comment = (yp_comment_t) {
Expand Down Expand Up @@ -13796,6 +13796,8 @@ yp_parser_init(yp_parser_t *parser, const uint8_t *source, size_t size, const ch
.newline_list = YP_NEWLINE_LIST_EMPTY
};

yp_allocator_init(&parser->allocator, 0);

yp_accepts_block_stack_push(parser, true);

// Initialize the constant pool. We're going to completely guess as to the
Expand Down Expand Up @@ -13856,14 +13858,14 @@ yp_parser_register_encoding_decode_callback(yp_parser_t *parser, yp_encoding_dec

// Free all of the memory associated with the comment list.
static inline void
yp_comment_list_free(yp_list_t *list) {
yp_comment_list_free(yp_allocator_t *allocator, yp_list_t *list) {
yp_list_node_t *node, *next;

for (node = list->head; node != NULL; node = next) {
next = node->next;

yp_comment_t *comment = (yp_comment_t *) node;
free(comment);
yp_free(allocator, comment);
}
}

Expand All @@ -13873,9 +13875,10 @@ yp_parser_free(yp_parser_t *parser) {
yp_string_free(&parser->filepath_string);
yp_diagnostic_list_free(&parser->error_list);
yp_diagnostic_list_free(&parser->warning_list);
yp_comment_list_free(&parser->comment_list);
yp_comment_list_free(&parser->allocator, &parser->comment_list);
yp_constant_pool_free(&parser->constant_pool);
yp_newline_list_free(&parser->newline_list);
yp_allocator_free(&parser->allocator);

while (parser->lex_modes.index >= YP_LEX_STACK_SIZE) {
lex_mode_pop(parser);
Expand Down
2 changes: 1 addition & 1 deletion templates/src/node.c.erb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ yp_node_destroy(yp_parser_t *parser, yp_node_t *node) {
assert(false && "unreachable");
break;
}
free(node);
yp_free(&parser->allocator, node);
}

static void
Expand Down
Loading