add assignment operator and unify memory pools

The unification of memory pools also fixed some memory leaks and
hopefully reduced the mallocs of identifier strings significantly by
giving them the same pool as the token stream.
This commit is contained in:
r4 2021-12-21 11:40:49 +01:00
parent 21694f98ac
commit 63af3e907b
11 changed files with 121 additions and 48 deletions

View File

@ -24,10 +24,16 @@ map_test: map_test.c util.c map.c
run_map_test: map_test
valgrind ./map_test
pool_test: pool_test.c util.c
$(CC) -o $@ $< $(CFLAGS) $(LDFLAGS)
run_pool_test: pool_test
valgrind ./pool_test
.PHONY: clean
clean:
rm -f $(OBJ) $(EXE) deps.mk gmon.out map_test
rm -f $(OBJ) $(EXE) deps.mk gmon.out map_test pool_test
ifneq ($(MAKECMDGOALS),clean)
include deps.mk

View File

@ -1,7 +1,13 @@
a := 1
b := 1 - 2 * 2 + 5
c := a + b * 2 * b
d := a + 4 * b * a
a = a + 1
b := a - 2 * 3
b = 2 + b * a
c := -b
//a := 1
//b := 1 - 2 * 2 + 5
//c := a + b * 2 * b
//d := a + 4 * b * a
/*x := 1
y := 1

2
lex.c
View File

@ -61,7 +61,7 @@ TokList lex(const char *s) {
.kind = TokIdent,
.Ident = {
.kind = IdentName,
.Name = sndup(start, i),
.Name = psndup(toks.p, start, i),
},
});
}

2
map.c
View File

@ -85,7 +85,7 @@ bool map_insert(Map *m, const char *key, const void *val) {
return replaced;
}
bool map_get(Map *m, const char *key, void *out_val) {
bool map_get(const Map *m, const char *key, void *out_val) {
size_t idx = fnv1a32(key, strlen(key)) & (m->cap - 1);
for (;;) {
if (m->slots[idx].empty)

2
map.h
View File

@ -26,6 +26,6 @@ void map_term(Map *m);
bool map_insert(Map *m, const char *key, const void *val);
/* Returns true if the key was found, returns false if it wasn't found.
* out_val may be set to NULL. */
bool map_get(Map *m, const char *key, void *out_val);
bool map_get(const Map *m, const char *key, void *out_val);
#endif /* MAP_H */

38
parse.c
View File

@ -18,6 +18,7 @@ typedef struct Scope {
} Scope;
static void mark_err(const Tok *t);
static size_t get_ident_addr(const Scope *sc, const char *name, const Tok *errpos);
static IRParam tok_to_irparam(Scope *sc, Tok *t);
static Scope make_scope(Scope *parent, size_t mem_addr, bool with_idents);
static void term_scope(Scope *sc);
@ -29,23 +30,29 @@ static void mark_err(const Tok *t) {
err_col = t->col;
}
static size_t get_ident_addr(const Scope *sc, const char *name, const Tok *errpos) {
size_t addr;
bool exists = false;
for (const Scope *i = sc; i != NULL; i = i->parent) {
if (!i->has_idents)
continue;
exists = map_get(&i->ident_addrs, name, &addr);
if (exists)
break;
}
if (!exists) {
mark_err(errpos);
set_err("Identifier '%s' not recognized in this scope", name);
return 0;
}
return addr;
}
static IRParam tok_to_irparam(Scope *sc, Tok *t) {
if (t->kind == TokIdent) {
size_t addr;
if (t->Ident.kind == IdentName) {
bool exists = false;
for (Scope *i = sc; i != NULL; i = i->parent) {
if (!i->has_idents)
continue;
exists = map_get(&i->ident_addrs, t->Ident.Name, &addr);
if (exists)
break;
}
if (!exists) {
mark_err(t);
set_err("Identifier '%s' not recognized in this scope", t->Ident.Name);
return (IRParam){0};
}
TRY_RET(addr = get_ident_addr(sc, t->Ident.Name, t), (IRParam){0});
} else if (t->Ident.kind == IdentAddr)
addr = t->Ident.Addr;
else
@ -316,6 +323,7 @@ static void stmt(State *s, Scope *sc, TokListItem *t) {
char *name = t->tok.Ident.Name;
t = t->next;
if (t->tok.kind == TokDeclare) {
t = t->next;
size_t addr = sc->mem_addr++;
bool replaced = map_insert(&sc->ident_addrs, name, &addr);
if (replaced) {
@ -323,7 +331,11 @@ static void stmt(State *s, Scope *sc, TokListItem *t) {
set_err("'%s' already declared in this scope", name);
return;
}
TRY(expr(s, sc, t, true, true, addr));
} else if (t->tok.kind == TokAssign) {
t = t->next;
size_t addr;
TRY(addr = get_ident_addr(sc, name, &start->tok));
TRY(expr(s, sc, t, true, true, addr));
}
}

18
pool_test.c Normal file
View File

@ -0,0 +1,18 @@
#include "util.c"
typedef struct Test {
char a_string[64];
} Test;
int main(void) {
Pool *p = pool_new(1);
Test *t = pool_alloc(p, sizeof(Test));
strcpy(t->a_string, "a test string");
Test *tarr = pool_alloc(p, sizeof(Test) * 32);
strcpy(tarr[31].a_string, "another test string");
char *c = pool_alloc(p, 1);
*c = 'a';
Test *largearr = pool_alloc(p, sizeof(Test) * 1024);
strcpy(largearr[1023].a_string, "yet another test string");
pool_term(p);
}

27
tok.c
View File

@ -38,40 +38,21 @@ const char *tok_str[TokKindEnumSize] = {
[TokWhile] = "while",
};
#define TOKLIST_MEMPOOL_INIT_CAP 4096
#define TOKLIST_MEMPOOL_INIT_CAP 32768
static inline TokListItem *toklist_alloc_item(TokList *l) {
if (l->curr_mempool_cap < l->mempool_sizes[l->curr_mempool]+1) {
if (l->curr_mempool+1 >= 32)
ASSERT_UNREACHED();
l->curr_mempool++;
l->curr_mempool_cap *= 2;
l->mempool_sizes[l->curr_mempool] = 0;
l->mempools[l->curr_mempool] = malloc(sizeof(TokListItem) * l->curr_mempool_cap);
}
TokListItem *itm = l->mempools[l->curr_mempool] + l->mempool_sizes[l->curr_mempool]++;
TokListItem *itm = pool_alloc(l->p, sizeof(TokListItem));
itm->prev = itm->next = NULL;
return itm;
}
void toklist_init(TokList *l) {
l->begin = l->end = NULL;
l->curr_mempool = 0;
l->mempools[l->curr_mempool] = malloc(sizeof(TokListItem) * TOKLIST_MEMPOOL_INIT_CAP);
l->curr_mempool_cap = TOKLIST_MEMPOOL_INIT_CAP;
l->mempool_sizes[0] = 0;
l->p = pool_new(TOKLIST_MEMPOOL_INIT_CAP);
}
void toklist_term(TokList *l) {
for (size_t i = 0; i <= l->curr_mempool; i++) {
for (size_t j = 0; j < l->mempool_sizes[i]; j++) {
TokListItem *itm = &l->mempools[i][j];
if (itm->tok.kind == TokIdent && itm->tok.Ident.kind == IdentName) {
free(itm->tok.Ident.Name);
}
}
free(l->mempools[i]);
}
pool_term(l->p);
}
void toklist_append(TokList *l, Tok t) {

7
tok.h
View File

@ -4,6 +4,8 @@
#include <stdint.h>
#include <unistd.h>
#include "util.h"
typedef struct Type {
enum {
TypeVoid = 0,
@ -87,10 +89,7 @@ typedef struct TokListItem {
typedef struct TokList {
TokListItem *begin, *end;
TokListItem *mempools[32]; /* few mallocs, no copying => much speed */
size_t mempool_sizes[32];
size_t curr_mempool_cap;
size_t curr_mempool;
Pool *p;
} TokList;
void toklist_init(TokList *l);

39
util.c
View File

@ -49,6 +49,38 @@ void set_err(const char *fmt, ...) {
va_end(va);
}
Pool *pool_new(size_t init_cap) {
Pool *p = malloc(sizeof(Pool) + init_cap);
p->len = 0;
p->cap = init_cap;
p->data = p + 1;
p->next = NULL;
return p;
}
void pool_term(Pool *p) {
for (Pool *i = p; i != NULL;) {
Pool *next = i->next;
free(i);
i = next;
}
}
void *pool_alloc(Pool *p, size_t bytes) {
for (Pool *i = p;; i = i->next) {
if (i->len + bytes < i->cap) {
void *ret = (uint8_t*)i->data + i->len;
i->len += bytes;
return ret;
}
if (!i->next) {
i->next = pool_new(bytes + i->cap * 2);
i->next->len = bytes;
return i->next->data;
}
}
}
char *sndup(const char *s, size_t n) {
char *ret = malloc(n+1);
if (ret) {
@ -58,6 +90,13 @@ char *sndup(const char *s, size_t n) {
return ret;
}
char *psndup(Pool *p, const char *s, size_t n) {
char *ret = pool_alloc(p, n+1);
memcpy(ret, s, n);
ret[n] = 0;
return ret;
}
intmax_t stoimax(const char *s, size_t n, size_t base, ssize_t *endpos) {
for (size_t i = 0; i < n; i++) { if (s[i] == 0) { n = i; break; } }
intmax_t res = 0;

14
util.h
View File

@ -41,11 +41,23 @@ void set_err(const char *fmt, ...);
#define IS_ALPHA(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
#define IS_ALNUM(c) (IS_ALPHA(c) || IS_NUM(c))
/* Useful for efficiently allocating lots of data that can all be freed at once afterwards. */
typedef struct Pool {
struct Pool *next;
void *data;
size_t len, cap;
} Pool;
Pool *pool_new(size_t init_cap); /* You usually want init_cap to be pretty high. */
void pool_term(Pool *p);
void *pool_alloc(Pool *p, size_t bytes);
#define streq(a, b) (strcmp(a, b) == 0)
/* check if a null-terminated string and a non-null-terminated string are equal */
static inline bool streq_0_n(const char *a, const char *b, size_t bn) { return bn == strlen(a) ? strncmp(a, b, bn) == 0 : false; }
/* a more trusting version of strndup; also for systems that don't have strndup */
/* a more trusting version of strndup */
char *sndup(const char *s, size_t n);
/* sndup with memory pools */
char *psndup(Pool *p, const char *s, size_t n);
/* convert a non-null-terminated string to an intmax_t */
intmax_t stoimax(const char *s, size_t n, size_t base, ssize_t *endpos /* -1 on success */);
/* convert a non-null-terminated string to a double */