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
|
endif
|
||||||
|
|
||||||
# Units to compile
|
# 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))
|
OBJECTS:=$(patsubst src/%.c, $(DIST)/%.o, $(UNITS))
|
||||||
|
|
||||||
TEST_UNITS=test/main.c
|
TEST_UNITS=test/main.c
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#ifndef ALISP_H
|
#ifndef ALISP_H
|
||||||
#define ALISP_H
|
#define ALISP_H
|
||||||
|
|
||||||
|
#include <alisp/allocator.h>
|
||||||
#include <alisp/base.h>
|
#include <alisp/base.h>
|
||||||
#include <alisp/lisp.h>
|
#include <alisp/lisp.h>
|
||||||
#include <alisp/reader.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