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:
parent
21694f98ac
commit
63af3e907b
8
Makefile
8
Makefile
@ -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
|
||||
|
@ -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
2
lex.c
@ -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
2
map.c
@ -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
2
map.h
@ -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 */
|
||||
|
28
parse.c
28
parse.c
@ -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 IRParam tok_to_irparam(Scope *sc, Tok *t) {
|
||||
if (t->kind == TokIdent) {
|
||||
static size_t get_ident_addr(const Scope *sc, const char *name, const Tok *errpos) {
|
||||
size_t addr;
|
||||
if (t->Ident.kind == IdentName) {
|
||||
bool exists = false;
|
||||
for (Scope *i = sc; i != NULL; i = i->parent) {
|
||||
for (const Scope *i = sc; i != NULL; i = i->parent) {
|
||||
if (!i->has_idents)
|
||||
continue;
|
||||
exists = map_get(&i->ident_addrs, t->Ident.Name, &addr);
|
||||
exists = map_get(&i->ident_addrs, 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};
|
||||
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) {
|
||||
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
18
pool_test.c
Normal 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
27
tok.c
@ -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
7
tok.h
@ -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
39
util.c
@ -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
14
util.h
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user