allocator: implement a basic allocator
This commit is contained in:
3
Makefile
3
Makefile
@@ -19,7 +19,8 @@ CFLAGS=$(GFLAGS) $(DFLAGS) -DTEST_VERBOSE=1
|
||||
endif
|
||||
|
||||
# Units to compile
|
||||
UNITS=src/sv.c src/vec.c src/stream.c src/symtable.c src/lisp.c src/sys.c src/reader.c
|
||||
UNITS=src/sv.c src/vec.c src/stream.c src/symtable.c src/lisp.c src/allocator.c \
|
||||
src/sys.c src/reader.c
|
||||
OBJECTS:=$(patsubst src/%.c, $(DIST)/%.o, $(UNITS))
|
||||
|
||||
TEST_UNITS=test/main.c
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef ALISP_H
|
||||
#define ALISP_H
|
||||
|
||||
#include <alisp/allocator.h>
|
||||
#include <alisp/base.h>
|
||||
#include <alisp/lisp.h>
|
||||
#include <alisp/reader.h>
|
||||
|
||||
57
include/alisp/allocator.h
Normal file
57
include/alisp/allocator.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* allocator.h: Lisp Allocator
|
||||
* Created: 2026-02-12
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#ifndef ALLOCATOR_H
|
||||
#define ALLOCATOR_H
|
||||
|
||||
#include <alisp/lisp.h>
|
||||
#include <alisp/vec.h>
|
||||
|
||||
#define ALLOC_PAGE_DEFAULT_SIZE 512
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u64 references;
|
||||
tag_t tag : 8;
|
||||
} alloc_metadata_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
alloc_metadata_t metadata;
|
||||
u8 data[];
|
||||
} alloc_node_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec_t data;
|
||||
} page_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec_t pages;
|
||||
vec_t free_list;
|
||||
} arena_t;
|
||||
|
||||
lisp_t *arena_make(arena_t *, tag_t type);
|
||||
void arena_delete(arena_t *, lisp_t *);
|
||||
u64 arena_cost(arena_t *);
|
||||
void arena_free(arena_t *);
|
||||
|
||||
#endif
|
||||
|
||||
/* Copyright (C) 2026 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the GNU General
|
||||
* Public License Version 2, which you should have received a copy of along with
|
||||
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
175
src/allocator.c
Normal file
175
src/allocator.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/* allocator.c: Allocator implementations
|
||||
* Created: 2026-02-12
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <alisp/allocator.h>
|
||||
#include <alisp/lisp.h>
|
||||
#include <alisp/vec.h>
|
||||
#include <string.h>
|
||||
|
||||
page_t *make_page(u64 size)
|
||||
{
|
||||
page_t *page = calloc(1, sizeof(*page));
|
||||
vec_init(&page->data, MAX(size, ALLOC_PAGE_DEFAULT_SIZE));
|
||||
return page;
|
||||
}
|
||||
|
||||
alloc_node_t *make_node(page_t *page, tag_t type)
|
||||
{
|
||||
alloc_node_t *node = NULL;
|
||||
u64 size = sizeof(*node);
|
||||
switch (type)
|
||||
{
|
||||
case TAG_CONS:
|
||||
size += sizeof(cons_t);
|
||||
break;
|
||||
case TAG_VEC:
|
||||
size += sizeof(vec_t);
|
||||
break;
|
||||
case TAG_NIL:
|
||||
case TAG_INT:
|
||||
case TAG_SYM:
|
||||
default:
|
||||
FAIL("Unreachable");
|
||||
return node;
|
||||
}
|
||||
|
||||
// We must ensure size is a multiple of 8 for alignment purposes
|
||||
size = (size & 0b111) == 0 ? size : size + (8 - (size & 0b111));
|
||||
|
||||
if (!vec_try_append(&page->data, NULL, size))
|
||||
return NULL;
|
||||
node = (alloc_node_t *)(vec_data(&page->data) + page->data.size - size);
|
||||
node->metadata = (alloc_metadata_t){.references = 0, .tag = type};
|
||||
return node;
|
||||
}
|
||||
|
||||
alloc_node_t *lisp_to_node(lisp_t *lisp)
|
||||
{
|
||||
void *raw_ptr = NULL;
|
||||
switch (get_tag(lisp))
|
||||
{
|
||||
case TAG_CONS:
|
||||
raw_ptr = as_cons(lisp);
|
||||
break;
|
||||
case TAG_VEC:
|
||||
raw_ptr = as_vec(lisp);
|
||||
break;
|
||||
case TAG_NIL: // These shouldn't be allocated
|
||||
case TAG_INT:
|
||||
case TAG_SYM:
|
||||
default:
|
||||
FAIL("Unreachable");
|
||||
return NIL;
|
||||
}
|
||||
|
||||
alloc_metadata_t *data = raw_ptr;
|
||||
return (alloc_node_t *)(&data[-1]);
|
||||
}
|
||||
|
||||
lisp_t *arena_make(arena_t *arena, tag_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TAG_CONS:
|
||||
case TAG_VEC:
|
||||
break;
|
||||
case TAG_NIL: // These shouldn't be allocated
|
||||
case TAG_INT:
|
||||
case TAG_SYM:
|
||||
default:
|
||||
FAIL("Unreachable");
|
||||
return NIL;
|
||||
}
|
||||
|
||||
for (u64 i = 0; i < VEC_SIZE(&arena->pages, page_t *); ++i)
|
||||
{
|
||||
page_t *page = VEC_GET(&arena->pages, i, page_t *);
|
||||
alloc_node_t *node = make_node(page, type);
|
||||
if (node)
|
||||
{
|
||||
return tag_generic(node->data, type);
|
||||
}
|
||||
}
|
||||
|
||||
// We'll have to make a new page to satisfy the size requirements.
|
||||
page_t *page = make_page(0);
|
||||
vec_append(&arena->pages, &page, sizeof(page));
|
||||
alloc_node_t *node = make_node(page, type);
|
||||
|
||||
if (!node)
|
||||
{
|
||||
FAIL("Unexpected issue with allocating to a verifiably good page");
|
||||
}
|
||||
return tag_generic(node->data, type);
|
||||
}
|
||||
|
||||
void arena_delete(arena_t *arena, lisp_t *lisp)
|
||||
{
|
||||
alloc_node_t *node = lisp_to_node(lisp);
|
||||
assert(node && node->metadata.references == 0);
|
||||
vec_append(&arena->free_list, &node, sizeof(node));
|
||||
}
|
||||
|
||||
u64 arena_cost(arena_t *arena)
|
||||
{
|
||||
u64 total_size = arena->pages.size;
|
||||
for (u64 i = 0; i < VEC_SIZE(&arena->pages, page_t *); ++i)
|
||||
{
|
||||
page_t *page = VEC_GET(&arena->pages, i, page_t *);
|
||||
total_size += page->data.size;
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
void arena_free(arena_t *arena)
|
||||
{
|
||||
for (u64 i = 0; i < VEC_SIZE(&arena->pages, page_t *); ++i)
|
||||
{
|
||||
page_t *page = VEC_GET(&arena->pages, i, page_t *);
|
||||
// Iterate through every alloc_node in this page
|
||||
for (u64 j = 0; j < VEC_SIZE(&page->data, u8);)
|
||||
{
|
||||
alloc_node_t *node = (alloc_node_t *)(vec_data(&page->data) + j);
|
||||
u64 next = sizeof(*node) + tag_sizeof(node->metadata.tag);
|
||||
switch (node->metadata.tag)
|
||||
{
|
||||
case TAG_CONS:
|
||||
// Do nothing - will be cleaned by overall vec free anyway
|
||||
break;
|
||||
case TAG_VEC:
|
||||
vec_free((vec_t *)node->data);
|
||||
break;
|
||||
case TAG_NIL:
|
||||
case TAG_INT:
|
||||
case TAG_SYM:
|
||||
default:
|
||||
FAIL("Unreachable");
|
||||
}
|
||||
j += next;
|
||||
}
|
||||
vec_free(&page->data);
|
||||
free(page);
|
||||
}
|
||||
vec_free(&arena->pages);
|
||||
vec_free(&arena->free_list);
|
||||
memset(arena, 0, sizeof(*arena));
|
||||
}
|
||||
|
||||
/* Copyright (C) 2026 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the GNU General
|
||||
* Public License Version 2, which you should have received a copy of along with
|
||||
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
Reference in New Issue
Block a user