Compare commits

...

4 Commits

Author SHA1 Message Date
Aryadev Chavali
042cc48e8c lisp: split memory into its own structure 2026-02-12 22:51:29 +00:00
Aryadev Chavali
b51aaa3d65 lisp: replace sys_register with sys_alloc
Allows us to abstract allocation away, creating Lisps automatically.
2026-02-12 22:51:29 +00:00
Aryadev Chavali
6499a9dd6d lisp: combine tag.h into lisp.h 2026-02-12 22:51:29 +00:00
Aryadev Chavali
c1cdb8607d lisp: split memory into conses and vectors
During garbage collection, we'll iterate through these two vectors.
Instead of freeing the memory, we can swap cells in the vector and
decrement its size.

The idea is we can attach an allocator to the system where we reuse
memory instead of just allocating everytime.
2026-02-12 22:51:29 +00:00
7 changed files with 120 additions and 113 deletions

View File

@@ -14,7 +14,6 @@
#include <alisp/stream.h> #include <alisp/stream.h>
#include <alisp/sv.h> #include <alisp/sv.h>
#include <alisp/symtable.h> #include <alisp/symtable.h>
#include <alisp/tag.h>
#include <alisp/vec.h> #include <alisp/vec.h>
#endif #endif

View File

@@ -23,15 +23,65 @@ typedef struct
lisp_t *car, *cdr; lisp_t *car, *cdr;
} cons_t; } cons_t;
/// Tagging system
typedef enum Tag
{
TAG_NIL = 0b00000000, // Start of atomic types
TAG_INT = 0b00000001, // Special tag so we can encode 63 bit integers
TAG_SYM = 0b00000100,
TAG_CONS = 0b00000010, // Start of container types
TAG_VEC = 0b00000110,
NUM_TAGS = 5,
} tag_t;
static_assert(NUM_TAGS == 5, "Expected NUM_TAGS == 5 for enum SHIFT");
enum Shift
{
SHIFT_INT = 1,
SHIFT_SYM = 8,
SHIFT_CONS = 8,
SHIFT_VEC = 8,
};
static_assert(NUM_TAGS == 5, "Expected NUM_TAGS == 5 for enum MASK");
enum Mask
{
MASK_INT = 0b00000001,
MASK_SYM = 0b11111111,
MASK_CONS = 0b11111111,
MASK_VEC = 0b11111111,
};
// Some helper macros for tagging
#define TAG(PTR, TYPE) ((lisp_t *)(((PTR) << SHIFT_##TYPE) | TAG_##TYPE))
#define IS_TAG(PTR, TYPE) (((u64)(PTR) & MASK_##TYPE) == TAG_##TYPE)
#define UNTAG(PTR, TYPE) (((u64)PTR) >> SHIFT_##TYPE)
#define INT_MAX ((((i64)1) << 62) - 1)
#define INT_MIN (-(((i64)1) << 62))
tag_t get_tag(const lisp_t *);
lisp_t *tag_int(const i64);
lisp_t *tag_sym(const char *);
lisp_t *tag_cons(const cons_t *);
lisp_t *tag_vec(const vec_t *);
/// System context /// System context
typedef struct typedef struct
{ {
vec_t memory; vec_t conses;
vec_t vectors;
u64 num_conses, num_vectors;
} sys_mem_t;
typedef struct
{
sys_mem_t memory;
sym_table_t symtable; sym_table_t symtable;
} sys_t; } sys_t;
void sys_init(sys_t *); void sys_init(sys_t *);
void sys_register(sys_t *, lisp_t *); lisp_t *sys_alloc(sys_t *, tag_t type);
void sys_free(sys_t *); void sys_free(sys_t *);
// Debugging function: provides total memory usage from system. // Debugging function: provides total memory usage from system.

View File

@@ -1,68 +0,0 @@
/* tag.h: Pointer tagging
* Created: 2026-02-04
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
*/
#ifndef TAG_H
#define TAG_H
#include <alisp/lisp.h>
typedef enum Tag
{
TAG_NIL = 0b00000000, // Start of atomic types
TAG_INT = 0b00000001, // Special tag so we can encode 63 bit integers
TAG_SYM = 0b00000100,
TAG_CONS = 0b00000010, // Start of container types
TAG_VEC = 0b00000110,
NUM_TAGS = 5,
} tag_t;
static_assert(NUM_TAGS == 5, "Expected NUM_TAGS == 5 for enum SHIFT");
enum Shift
{
SHIFT_INT = 1,
SHIFT_SYM = 8,
SHIFT_CONS = 8,
SHIFT_VEC = 8,
};
static_assert(NUM_TAGS == 5, "Expected NUM_TAGS == 5 for enum MASK");
enum Mask
{
MASK_INT = 0b00000001,
MASK_SYM = 0b11111111,
MASK_CONS = 0b11111111,
MASK_VEC = 0b11111111,
};
// Some helper macros for tagging
#define TAG(PTR, TYPE) ((lisp_t *)(((PTR) << SHIFT_##TYPE) | TAG_##TYPE))
#define IS_TAG(PTR, TYPE) (((u64)(PTR) & MASK_##TYPE) == TAG_##TYPE)
#define UNTAG(PTR, TYPE) (((u64)PTR) >> SHIFT_##TYPE)
#define INT_MAX ((((i64)1) << 62) - 1)
#define INT_MIN (-(((i64)1) << 62))
tag_t get_tag(const lisp_t *);
lisp_t *tag_int(const i64);
lisp_t *tag_sym(const char *);
lisp_t *tag_cons(const cons_t *);
lisp_t *tag_vec(const vec_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/>.
*/

View File

@@ -9,41 +9,75 @@
#include <string.h> #include <string.h>
#include <alisp/lisp.h> #include <alisp/lisp.h>
#include <alisp/tag.h>
void sys_init(sys_t *sys) void sys_init(sys_t *sys)
{ {
memset(sys, 0, sizeof(*sys)); memset(sys, 0, sizeof(*sys));
} }
void sys_register(sys_t *sys, lisp_t *ptr) lisp_t *sys_alloc(sys_t *sys, tag_t type)
{ {
// Simply append it to the list of currently active conses switch (type)
vec_append(&sys->memory, &ptr, sizeof(&ptr)); {
case TAG_CONS:
{
cons_t *cons = calloc(1, sizeof(*cons));
lisp_t *lisp = tag_cons(cons);
vec_append(&sys->memory.conses, &lisp, sizeof(&lisp));
sys->memory.num_conses++;
return lisp;
}
case TAG_VEC:
{
vec_t *vec = calloc(1, sizeof(*vec));
lisp_t *lisp = tag_vec(vec);
vec_append(&sys->memory.vectors, &lisp, sizeof(&lisp));
sys->memory.num_vectors++;
return lisp;
}
// Shouldn't be registered
case TAG_NIL:
case TAG_INT:
case TAG_SYM:
default:
FAIL("Unreachable");
}
return NIL;
} }
u64 sys_cost(sys_t *sys) u64 sys_cost(sys_t *sys)
{ {
return sym_table_cost(&sys->symtable) + sys->memory.capacity; u64 vec_capacity = 0;
for (u64 i = 0; i < sys->memory.num_vectors; ++i)
{
lisp_t *vec = VEC_GET(&sys->memory.vectors, i, lisp_t *);
vec_capacity += as_vec(vec)->capacity;
}
return sym_table_cost(&sys->symtable) +
(sys->memory.num_conses * sizeof(cons_t)) + vec_capacity;
} }
void sys_free(sys_t *sys) void sys_free(sys_t *sys)
{ {
static_assert(NUM_TAGS == 5);
sym_table_free(&sys->symtable); sym_table_free(&sys->symtable);
if (sys->memory.size == 0)
return;
// Iterate through each cell of memory currently allocated and free them // Iterate through each cell of memory currently allocated and free them
for (size_t i = 0; i < VEC_SIZE(&sys->memory, lisp_t **); ++i) for (size_t i = 0; i < VEC_SIZE(&sys->memory.conses, lisp_t **); ++i)
{ {
lisp_t *allocated = VEC_GET(&sys->memory, i, lisp_t *); lisp_t *allocated = VEC_GET(&sys->memory.conses, i, lisp_t *);
lisp_free(allocated); lisp_free(allocated);
} }
// Free the container // Iterate through each cell of memory currently allocated and free them
vec_free(&sys->memory); for (size_t i = 0; i < VEC_SIZE(&sys->memory.vectors, lisp_t **); ++i)
{
lisp_t *allocated = VEC_GET(&sys->memory.vectors, i, lisp_t *);
lisp_free(allocated);
}
// Free the containers
vec_free(&sys->memory.conses);
vec_free(&sys->memory.vectors);
// Ensure no one treats this as active in any sense // Ensure no one treats this as active in any sense
memset(sys, 0, sizeof(*sys)); memset(sys, 0, sizeof(*sys));
@@ -56,22 +90,17 @@ lisp_t *make_int(i64 i)
lisp_t *cons(sys_t *sys, lisp_t *car, lisp_t *cdr) lisp_t *cons(sys_t *sys, lisp_t *car, lisp_t *cdr)
{ {
cons_t *cons = calloc(1, sizeof(*cons)); lisp_t *cons = sys_alloc(sys, TAG_CONS);
cons->car = car; CAR(cons) = car;
cons->cdr = cdr; CDR(cons) = cdr;
return cons;
lisp_t *lcons = tag_cons(cons);
sys_register(sys, lcons);
return lcons;
} }
lisp_t *make_vec(sys_t *sys, u64 capacity) lisp_t *make_vec(sys_t *sys, u64 capacity)
{ {
vec_t *vec = calloc(1, sizeof(*vec)); lisp_t *vec = sys_alloc(sys, TAG_VEC);
vec_init(vec, capacity); vec_init(as_vec(vec), capacity);
lisp_t *ptr = tag_vec(vec); return vec;
sys_register(sys, ptr);
return ptr;
} }
lisp_t *intern(sys_t *sys, sv_t sv) lisp_t *intern(sys_t *sys, sv_t sv)

View File

@@ -9,7 +9,6 @@
#include <string.h> #include <string.h>
#include <alisp/reader.h> #include <alisp/reader.h>
#include <alisp/tag.h>
const char *read_err_to_cstr(read_err_t err) const char *read_err_to_cstr(read_err_t err)
{ {

View File

@@ -8,7 +8,7 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <alisp/tag.h> #include <alisp/lisp.h>
lisp_t *tag_int(i64 i) lisp_t *tag_int(i64 i)
{ {

View File

@@ -151,44 +151,42 @@ void sys_test(void)
TEST_START(); TEST_START();
sys_t sys = {0}; sys_t sys = {0};
sys_init(&sys); sys_init(&sys);
u64 old_memory_size = sys.memory.size; u64 old_memory_size = sys_cost(&sys);
// Creating integers doesn't affect memory size // Creating integers doesn't affect memory size
(void)make_int(2000); (void)make_int(2000);
TEST(sys.memory.size == old_memory_size, TEST(sys_cost(&sys) == old_memory_size,
"Making integers doesn't affect system memory size"); "Making integers doesn't affect system memory size");
// Creating symbols won't affect memory size, but does affect the symbol table // Creating symbols does affect memory size and memory table
(void)intern(&sys, SV_AUTO("hello world!")); (void)intern(&sys, SV_AUTO("hello world!"));
TEST(sys.memory.size == old_memory_size, TEST(sys_cost(&sys) > old_memory_size,
"Interning doesn't affect system memory size"); "Interning doesn't affect system memory size");
TEST(sys.symtable.count > 0, "Interning affects symbol table"); TEST(sys.symtable.count > 0, "Interning affects symbol table");
old_memory_size = sys_cost(&sys);
// Creating conses do affect memory size // Creating conses do affect memory size
(void)cons(&sys, make_int(1), make_int(2)); (void)cons(&sys, make_int(1), make_int(2));
TEST(sys.memory.size > 0, "Creating conses affects memory size"); TEST(sys_cost(&sys) > 0, "Creating conses affects memory size");
old_memory_size = sys.memory.size; old_memory_size = sys_cost(&sys);
(void)cons(&sys, intern(&sys, SV_AUTO("test")), NIL); (void)cons(&sys, intern(&sys, SV_AUTO("test")), NIL);
TEST(sys.memory.size > old_memory_size, TEST(sys_cost(&sys) > old_memory_size,
"Creating conses back to back affects memory size"); "Creating conses back to back affects memory size");
old_memory_size = sys.memory.size; old_memory_size = sys_cost(&sys);
// Creating vectors does affect memory size // Creating vectors does affect memory size
(void)make_vec(&sys, 8); (void)make_vec(&sys, 8);
TEST(sys.memory.size > old_memory_size, TEST(sys_cost(&sys) > old_memory_size,
"Creating vectors (size 8) affects memory size"); "Creating vectors (size 8) affects memory size");
old_memory_size = sys.memory.size; old_memory_size = sys_cost(&sys);
(void)make_vec(&sys, 1000); (void)make_vec(&sys, 1000);
TEST(sys.memory.size > old_memory_size, TEST(sys_cost(&sys) > old_memory_size,
"Creating vectors (size 1000) affects memory size"); "Creating vectors (size 1000) affects memory size");
old_memory_size = sys.memory.size; old_memory_size = sys_cost(&sys);
sys_free(&sys); sys_free(&sys);
TEST(sys.memory.size == 0, "sys_free cleans up memory (shallow check)");
TEST(sys.symtable.count == 0, "sys_free cleans up symtable (shallow check)");
TEST_END(); TEST_END();
} }