diff --git a/CMakeLists.txt b/CMakeLists.txt index 948b432..ea96165 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,17 +31,12 @@ else() INTERFACE m) endif() -include(FetchContent) -FetchContent_Declare(gc - GIT_REPOSITORY https://github.com/ivmai/bdwgc.git - GIT_TAG v8.2.6) -FetchContent_MakeAvailable(gc) - set(PAW_SOURCE_DIR ${PROJECT_SOURCE_DIR}/src) add_library(paw STATIC) target_sources(paw - PUBLIC ${PAW_SOURCE_DIR}/api.h + PUBLIC ${PAW_SOURCE_DIR}/alloc.h + ${PAW_SOURCE_DIR}/api.h ${PAW_SOURCE_DIR}/ast.h ${PAW_SOURCE_DIR}/auxlib.h ${PAW_SOURCE_DIR}/call.h @@ -49,13 +44,12 @@ target_sources(paw ${PAW_SOURCE_DIR}/compile.h ${PAW_SOURCE_DIR}/debug.h ${PAW_SOURCE_DIR}/env.h - ${PAW_SOURCE_DIR}/gc_aux.h + ${PAW_SOURCE_DIR}/gc.h ${PAW_SOURCE_DIR}/hir.h ${PAW_SOURCE_DIR}/lex.h ${PAW_SOURCE_DIR}/lib.h ${PAW_SOURCE_DIR}/map.h ${PAW_SOURCE_DIR}/mem.h - ${PAW_SOURCE_DIR}/meta.h ${PAW_SOURCE_DIR}/opcode.h ${PAW_SOURCE_DIR}/os.h ${PAW_SOURCE_DIR}/parse.h @@ -67,17 +61,17 @@ target_sources(paw ${PAW_SOURCE_DIR}/unify.h ${PAW_SOURCE_DIR}/util.h ${PAW_SOURCE_DIR}/value.h - PRIVATE ${PAW_SOURCE_DIR}/api.c + PRIVATE ${PAW_SOURCE_DIR}/alloc.c + ${PAW_SOURCE_DIR}/api.c ${PAW_SOURCE_DIR}/ast.c ${PAW_SOURCE_DIR}/auxlib.c ${PAW_SOURCE_DIR}/call.c - ${PAW_SOURCE_DIR}/check.c ${PAW_SOURCE_DIR}/code.c ${PAW_SOURCE_DIR}/codegen.c ${PAW_SOURCE_DIR}/compile.c ${PAW_SOURCE_DIR}/debug.c ${PAW_SOURCE_DIR}/env.c - ${PAW_SOURCE_DIR}/gc_aux.c + ${PAW_SOURCE_DIR}/gc.c ${PAW_SOURCE_DIR}/hir.c #${PAW_SOURCE_DIR}/iolib.c ${PAW_SOURCE_DIR}/lex.c @@ -85,10 +79,10 @@ target_sources(paw #${PAW_SOURCE_DIR}/mathlib.c ${PAW_SOURCE_DIR}/map.c ${PAW_SOURCE_DIR}/mem.c - ${PAW_SOURCE_DIR}/meta.c ${PAW_SOURCE_DIR}/opcode.c ${PAW_SOURCE_DIR}/os.c ${PAW_SOURCE_DIR}/parse.c + ${PAW_SOURCE_DIR}/resolve.c ${PAW_SOURCE_DIR}/rt.c ${PAW_SOURCE_DIR}/str.c ${PAW_SOURCE_DIR}/type.c @@ -96,9 +90,6 @@ target_sources(paw ${PAW_SOURCE_DIR}/util.c ${PAW_SOURCE_DIR}/value.c ${PAW_SOURCE_DIR}/vector.c) -target_link_libraries(paw - PRIVATE paw_context - PUBLIC gc) target_include_directories(paw PUBLIC ${PAW_SOURCE_DIR}) target_compile_definitions(paw diff --git a/GRAMMER.md b/GRAMMER.md index f3812c0..e6ce8b9 100644 --- a/GRAMMER.md +++ b/GRAMMER.md @@ -1,7 +1,7 @@ # Paw language grammer (EBNF) **TODO: get rid of requirement that "break" | "return" | "continue" is the last statement in the block** ** just don't emit unreachable code** -** use a tool to validate this...** +** use a tool to validate this EBNF...** ## Module ```ebnf diff --git a/README.md b/README.md index b4c642b..522f7d7 100644 --- a/README.md +++ b/README.md @@ -324,13 +324,14 @@ assert(status != 0) + [x] type inference for `struct` templates and builtin containers + [x] sum types/discriminated unions (`enum`) + [x] product types (tuple) ++ [ ] refactor user-provided allocation interface to allow heap expansion + [ ] module system and `import` keyword + [ ] methods using `impl` blocks + [ ] error handling + [ ] type inference for `enum` templates + [ ] pattern matching (`switch` construct) + [ ] pattern matching (`if let`, `let` bindings) -+ [ ] custom garbage collector (using Boehm GC for now) ++ [x] custom garbage collector (using Boehm GC for now) + [ ] split off UTF-8 stuff into `String` structure, where `str` is a byte array and `String` is always valid UTF-8 + [ ] constness (`var` vs `let`) + [ ] compiler optimization passes diff --git a/src/alloc.c b/src/alloc.c new file mode 100644 index 0000000..1d8528a --- /dev/null +++ b/src/alloc.c @@ -0,0 +1,526 @@ +// Copyright (c) 2024, The paw Authors. All rights reserved. +// This source code is licensed under the MIT License, which can be found in +// LICENSE.md. See AUTHORS.md for a list of contributor names. +// +// NOTE: The block allocator code is from SQLite (mem3.c) + +#include "alloc.h" +#include "env.h" +#include "mem.h" + +#define MIN_RUNTIME_SIZE (32 * 1024) +#define MIN_HEAP_SIZE (aligned(sizeof(struct BlockAllocator)) + \ + aligned(sizeof(struct FastBins)) + \ + MIN_RUNTIME_SIZE) + +#define OS_MALLOC(P, size) ((P)->alloc((P)->ud, NULL, 0, size)) +#define OS_FREE(P, ptr, size) ((P)->alloc((P)->ud, ptr, size, 0)) + +#define BUMP(p, n) ERASE_TYPE(CAST_UPTR(p) + (n)) +#define ERASE_TYPE(p) CAST(p, void *) + +static inline paw_Bool is_pow2(size_t v) +{ + return v > 0 && !(v & (v >> 1)); +} + +static inline paw_Bool is_aligned(void *p, size_t align) +{ + return !(CAST_UPTR(p) & (align - 1)); +} + +static inline size_t aligned(size_t v) +{ + return v + (-v & (PAW_ALIGN - 1)); +} + +// Helpers for debugging when instrumented with ASan +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +int __asan_address_is_poisoned(void const volatile *addr); +void __asan_poison_memory_region(const volatile void *ptr, size_t size); +void __asan_unpoison_memory_region(const volatile void *ptr, size_t size); +# define POISON_MEMORY_REGION(p, z) __asan_poison_memory_region(p, z) +# define UNPOISON_MEMORY_REGION(p, z) __asan_unpoison_memory_region(p, z) +#else +# define POISON_MEMORY_REGION(p, z) +# define UNPOISON_MEMORY_REGION(p, z) +#endif + +#define BIN_FACTOR 8 +#define MIN_BIN_SIZE (sizeof(Value) * 2) +#define MAX_BIN_SIZE (MIN_BIN_SIZE * FAST_BIN_COUNT) +#define FLAGS_PER_BYTE 8 + +#define BIN_ID(size) CHECK_EXP((size) > 0 && aligned(size), ((size) - 1) / MIN_BIN_SIZE) +#define FLAG_BASE(H, uptr) CHECK_EXP(Z_IN_BOUNDS(H, uptr), ((uptr) - (H)->bounds[0]) / sizeof(void *)) +#define FLAG_ID(base) ((base) / FLAGS_PER_BYTE) +#define FLAG_BIT(base) ((base) % FLAGS_PER_BYTE) + +struct BinInfo { + struct BinInfo *prev; + char buffer[]; +}; + +struct BlockId { + uint32_t v; +}; + +struct Block { + union { + struct { + uint32_t prev_size; + uint32_t size4x; + } hdr; + struct { + struct BlockId prev; + struct BlockId next; + } list; + }; +}; + +#define B_HDR(L, i) (&(L)->blocks[(i) - 1].hdr) +#define B_LIST(L, i) (&(L)->blocks[i].list) + +#define BLOCK_NLISTS 61 +#define KEY_BLOCK_SHIFT 3 +#define BLOCK_SIZE sizeof(struct Block) +#define BAD_BLOCK 0 + +// Allocator for larger regions of memory +// Based off of mem3.c from SQLite +struct BlockAllocator { + struct BlockId free[BLOCK_NLISTS]; + struct Block *blocks; + size_t nblocks; + + paw_Alloc alloc; + paw_Env *P; + + struct BlockId key; + size_t key_size; + size_t key_min; +}; + + +uint8_t pawZ_get_flag(struct Heap *H, uintptr_t uptr) +{ + const size_t id = FLAG_BASE(H, uptr); + const uint8_t flag = H->flags[FLAG_ID(id)]; + return (flag >> FLAG_BIT(id)) & 1; +} + +void pawZ_set_flag(struct Heap *H, uintptr_t uptr) +{ + const size_t id = FLAG_BASE(H, uptr); + uint8_t *pflag = &H->flags[FLAG_ID(id)]; + paw_assert(0 == (*pflag & (1 << FLAG_BIT(id)))); + *pflag = *pflag | (1 << FLAG_BIT(id)); +} + +void pawZ_clear_flag(struct Heap *H, uintptr_t uptr) +{ + const size_t id = FLAG_BASE(H, uptr); + uint8_t *pflag = &H->flags[FLAG_ID(id)]; + paw_assert(0 != (*pflag & (1 << FLAG_BIT(id)))); + *pflag = *pflag & ~(1 << FLAG_BIT(id)); +} + +static void *block_malloc(struct BlockAllocator *, size_t); +static void block_free(struct BlockAllocator *, void *, size_t); + +static void init_block_allocator(struct BlockAllocator *a, void *heap, size_t heap_size) +{ + paw_assert(is_aligned(heap, PAW_ALIGN)); + memset(a->free, 0, sizeof(a->free)); + + assert(BLOCK_SIZE == 8); + a->blocks = heap; + a->nblocks = heap_size / BLOCK_SIZE - 2; + + a->key_size = a->nblocks; + a->key_min = a->key_size; + a->key.v = 1; + + B_HDR(a, 1)->size4x = (a->key_size << 2) + 2; + B_HDR(a, 1 + a->nblocks)->prev_size = a->nblocks; + B_HDR(a, 1 + a->nblocks)->size4x = 1; +} + +static struct Block *block_at(struct BlockAllocator *a, struct BlockId id) +{ + paw_assert(id.v < a->nblocks); + return &a->blocks[id.v]; +} + +static void unlink_from_list(struct BlockAllocator *a, struct BlockId id, struct BlockId *proot) +{ + struct Block *b = block_at(a, id); + const struct BlockId prev = b->list.prev; + const struct BlockId next = b->list.next; + if (prev.v != BAD_BLOCK) { + struct Block *prev_block = block_at(a, prev); + prev_block->list.next = next; + } else { + *proot = next; + } + if (next.v != BAD_BLOCK) { + struct Block *next_block = block_at(a, next); + next_block->list.prev = prev; + } + b->list.prev.v = 0; + b->list.next.v = 0; +} + +static void unlink_block(struct BlockAllocator *a, struct BlockId id) +{ + paw_assert((B_HDR(a, id.v)->size4x & 1) == 0); + paw_assert(id.v >= 1); + const size_t size = B_HDR(a, id.v)->size4x / 4; + paw_assert(size == B_HDR(a, id.v + size)->prev_size); + paw_assert(size >= 2); + const size_t hash = size % BLOCK_NLISTS; + unlink_from_list(a, id, &a->free[hash]); +} + +static void link_into_list(struct BlockAllocator *a, struct BlockId id, struct BlockId *proot) +{ + B_LIST(a, id.v)->next = *proot; + B_LIST(a, id.v)->prev.v = 0; + if (proot->v != BAD_BLOCK) { + B_LIST(a, proot->v)->prev = id; + } + *proot = id; +} + +static void link_block(struct BlockAllocator *a, struct BlockId id) +{ + paw_assert(id.v >= 1); + paw_assert((B_HDR(a, id.v)->size4x & 1) == 0); + const size_t size = B_HDR(a, id.v)->size4x / 4; + paw_assert(size == B_HDR(a, id.v + size)->prev_size); + paw_assert(size >= 2); + const size_t hash = size % BLOCK_NLISTS; + link_into_list(a, id, &a->free[hash]); +} + +static void fix_block_list(struct BlockAllocator *a, struct BlockId *proot) +{ + struct BlockId next; + + for (struct BlockId i = *proot; i.v != BAD_BLOCK; i = next){ + next = B_LIST(a, i.v)->next; + size_t size = B_HDR(a, i.v)->size4x; + paw_assert((size & 1) == 0); + if ((size & 2) == 0) { + unlink_from_list(a, i, proot); + paw_assert(i.v > B_HDR(a, i.v)->prev_size); + struct BlockId prev = {i.v - B_HDR(a, i.v)->prev_size}; + if (prev.v == next.v) { + next = B_LIST(a, prev.v)->next; + } + unlink_block(a, prev); + size = i.v + size / 4 - prev.v; + const uint32_t x = B_HDR(a, prev.v)->size4x & 2; + B_HDR(a, prev.v)->size4x = size * 4 | x; + B_HDR(a, prev.v + size)->prev_size = size; + link_block(a, prev); + i = prev; + } else { + size /= 4; + } + if (size > a->key_size) { + a->key_size = size; + a->key = i; + } + } +} + +static void *checkout_block(struct BlockAllocator *a, struct BlockId i, size_t nblocks) +{ + paw_assert(i.v >= 1); + paw_assert(B_HDR(a, i.v)->size4x / 4 == nblocks); + paw_assert(B_HDR(a, i.v + nblocks)->prev_size == nblocks); + const size_t x = B_HDR(a, i.v)->size4x; + B_HDR(a, i.v)->size4x = nblocks * 4 | 1 | (x & 2); + B_HDR(a, i.v + nblocks)->prev_size = nblocks; + B_HDR(a, i.v + nblocks)->size4x |= 2; + return &a->blocks[i.v]; +} + +static void *key_block_alloc(struct BlockAllocator *a, size_t nblocks) +{ + assert(a->key_size >= nblocks); + if (nblocks >= a->key_size - 1) { + void *p = checkout_block(a, a->key, a->key_size); + a->key.v = 0; + a->key_size = 0; + a->key_min = 0; + return p; + } else { + struct BlockId newi = {a->key.v + a->key_size - nblocks}; + assert(newi.v > a->key.v+1); + B_HDR(a, a->key.v + a->key_size)->prev_size = nblocks; + B_HDR(a, a->key.v + a->key_size)->size4x |= 2; + B_HDR(a, newi.v)->size4x = nblocks*4 + 1; + a->key_size -= nblocks; + B_HDR(a, newi.v)->prev_size = a->key_size; + const size_t x = B_HDR(a, a->key.v)->size4x & 2; + B_HDR(a, a->key.v)->size4x = a->key_size * 4 | x; + if (a->key_size < a->key_min) { + a->key_min = a->key_size; + } + return &a->blocks[newi.v]; + } +} + +#define COMPUTE_NBLOCKS(nbytes) CHECK_EXP(BLOCK_SIZE == 8 && (nbytes) > 12, \ + ((nbytes) + 11) / BLOCK_SIZE) + +// Modified from SQLite (mem3.c) +static void *block_malloc(struct BlockAllocator *a, size_t nbytes) +{ + size_t nblocks = COMPUTE_NBLOCKS(nbytes); + paw_assert(nblocks >= 2); + + // search for an exact fit + const size_t hash = nblocks % BLOCK_NLISTS; + for (struct BlockId id = a->free[hash]; + id.v != BAD_BLOCK; + id = B_LIST(a, id.v)->next) { + if (B_HDR(a, id.v)->size4x / 4 == nblocks) { + unlink_from_list(a, id, &a->free[hash]); + return checkout_block(a, id, nblocks); + } + } + + if (a->key_size >= nblocks) { + return key_block_alloc(a, nblocks); + } + + for (size_t to_free = nblocks * 16; + to_free < a->nblocks * 16; + to_free *= 2) { + if (a->key.v != BAD_BLOCK) { + link_block(a, a->key); + a->key.v = BAD_BLOCK; + a->key_size = 0; + } + for (size_t i = 0; i < BLOCK_NLISTS; ++i) { + fix_block_list(a, &a->free[i]); + } + if (a->key_size != 0) { + unlink_block(a, a->key); + if (a->key_size >= nblocks) { + return key_block_alloc(a, nblocks); + } + } + } + + return NULL; +} + +// Modified from SQLite (mem3.c) +static void block_free(struct BlockAllocator *a, void *ptr, size_t nbytes) +{ + size_t nblocks = COMPUTE_NBLOCKS(nbytes); + paw_assert(nblocks >= 2); + struct Block *b = ptr; + paw_assert(b > a->blocks && b < &a->blocks[a->nblocks]); + const struct BlockId i = {b - a->blocks}; + paw_assert((B_HDR(a, i.v)->size4x & 1) == 1); + paw_assert(nblocks == B_HDR(a, i.v)->size4x / 4); + paw_assert(i.v + nblocks <= a->nblocks + 1); + B_HDR(a, i.v)->size4x &= ~1; + B_HDR(a, i.v + nblocks)->prev_size = nblocks; + B_HDR(a, i.v + nblocks)->size4x &= ~2; + link_block(a, i); + + if (a->key.v != BAD_BLOCK) { + while ((B_HDR(a, a->key.v)->size4x & 2) == 0) { + nblocks = B_HDR(a, a->key.v)->prev_size; + a->key.v -= nblocks; + a->key_size += nblocks; + unlink_block(a, a->key); + const size_t x = B_HDR(a, a->key.v)->size4x & 2; + B_HDR(a, a->key.v)->size4x = a->key_size * 4 | x; + a->blocks[a->key.v + a->key_size-1].hdr.prev_size = a->key_size; + } + const size_t x = B_HDR(a, a->key.v)->size4x & 2; + while ((B_HDR(a, a->key.v + a->key_size)->size4x & 1) == 0) { + unlink_block(a, (struct BlockId){a->key.v + a->key_size}); + a->key_size += B_HDR(a, a->key.v + a->key_size)->size4x / 4; + B_HDR(a, a->key.v)->size4x = a->key_size * 4 | x; + B_HDR(a, a->key.v + a->key_size)->prev_size = a->key_size; + } + } +} + +static size_t block_size(void *ptr) +{ + paw_assert(ptr != NULL); + struct Block *b = ptr; + paw_assert((b[-1].hdr.size4x & 1) != 0); + return (b[-1].hdr.size4x & ~3) * 2 - 4; +} + +static void *alloc_uninit(struct FastBins *a, size_t id) +{ + void *ptr = a->uninit; + const size_t want = MIN_BIN_SIZE * (id + 1); + if (want > a->uninit_size) return NULL; + UNPOISON_MEMORY_REGION(a->uninit, want); + a->uninit = BUMP(a->uninit, want); + a->uninit_size -= want; + return ptr; +} + +static void *fast_malloc(struct FastBins *a, size_t size) +{ + paw_assert(0 < size && size <= MAX_BIN_SIZE); + const int id = BIN_ID(size); + struct BinInfo **pbin = &a->info[id]; + if (*pbin != NULL) { + struct BinInfo *bin = *pbin; + *pbin = bin->prev; + return ERASE_TYPE(bin); + } + return alloc_uninit(a, id); +} + +static void fast_free(struct FastBins *a, void *ptr, size_t size) +{ + if (ptr == NULL) return; + struct BinInfo *bin = CAST(ptr, struct BinInfo *); + const int id = BIN_ID(size); + *bin = (struct BinInfo){ + .prev = a->info[id], + }; + a->info[id] = bin; +} + +// TODO: Put everything in the same allocation +int pawZ_init(paw_Env *P, size_t heap_size) +{ + if (heap_size < MIN_HEAP_SIZE) return PAW_EVALUE; + const size_t nflags = heap_size / sizeof(void *); + const size_t zflags = nflags / FLAGS_PER_BYTE; + paw_assert(zflags * FLAGS_PER_BYTE == nflags); + + struct Heap *H = OS_MALLOC(P, sizeof(struct Heap) + zflags); + if (H == NULL) return PAW_EMEMORY; + *H = (struct Heap){ + .heap_size = heap_size, + .nflags = nflags, + .P = P, + }; + memset(H->flags, 0, zflags); + P->H = H; + + void *heap = OS_MALLOC(P, heap_size); + if (heap == NULL) goto no_memory; + H->heap = heap; + +#define SKIP_CHUNK(z) (heap = BUMP(heap, aligned(z)), \ + heap_size -= aligned(z)) + H->a_block = heap; + SKIP_CHUNK(sizeof(struct BlockAllocator)); + init_block_allocator(H->a_block, heap, heap_size); +#undef SKIP_CHUNK + + const size_t arena_size = heap_size / BIN_FACTOR; + void *arena = block_malloc(H->a_block, arena_size); + if (arena == NULL) goto no_memory; + POISON_MEMORY_REGION(arena, arena_size); + H->bins = (struct FastBins){ + .uninit_size = arena_size, + .arena_size = arena_size, + .uninit = arena, + .arena = arena, + }; + H->bounds[0] = CAST_UPTR(H->heap); + H->bounds[1] = H->bounds[0] + H->heap_size; + return PAW_OK; + +no_memory: + pawZ_uninit(P); + return PAW_EMEMORY; +} + +// TODO: option for detect_leaks(P); +#if 0 +static void detect_leaks(paw_Env *P) +{ + paw_Bool leak_detected = PAW_FALSE; + const struct Heap *H = P->H; + for (size_t i = 0; i < H->nflags; ++i) { + if (H->flags[i].value != 0) { + fprintf(stderr, "leak @ %p\n", ERASE_TYPE(H->bounds[0] + i * sizeof(void *))); + leak_detected = PAW_TRUE; + } + } + if (leak_detected) { + __builtin_trap(); + } + if (P->gc_bytes>0){ + printf("gc_bytes = %zu\n\n",P->gc_bytes); + } +} +#endif // 0 + +void pawZ_uninit(paw_Env *P) +{ + struct Heap *H = P->H; + if (H != NULL) { + if (H->bins.arena != NULL) { + UNPOISON_MEMORY_REGION(H->bins.uninit, H->bins.uninit_size); + block_free(P->H->a_block, H->bins.arena, H->bins.arena_size); + } + if (H->heap != NULL) OS_FREE(P, H->heap, H->heap_size); + OS_FREE(P, H, sizeof(*H) + H->nflags / FLAGS_PER_BYTE); + P->H = NULL; + } +} + +#define CHECK_UNUSED(H, ptr) paw_assert(!pawZ_get_flag(H, CAST_UPTR(ptr))) + +static void z_free(struct Heap *H, void *ptr, size_t size) +{ + size = aligned(size); + if (size <= MAX_BIN_SIZE) { + fast_free(&H->bins, ptr, size); + } else { + block_free(H->a_block, ptr, size); + } +} + +static void *z_malloc(struct Heap *H, size_t size) +{ + size = aligned(size); + void *ptr = size <= MAX_BIN_SIZE + ? fast_malloc(&H->bins, size) + : block_malloc(H->a_block, size); + return ptr; +} + +static void *z_realloc(struct Heap *H, void *old_ptr, size_t old_size, size_t new_size) +{ + void *new_ptr = z_malloc(H, new_size); + if (old_size > 0) { + const size_t copy_size = PAW_MIN(old_size, new_size); + memcpy(new_ptr, old_ptr, copy_size); + } + z_free(H, old_ptr, old_size); + return new_ptr; +} + +void *pawZ_alloc(paw_Env *P, void *ptr, size_t old_size, size_t new_size) +{ + struct Heap *H = P->H; + if (new_size == 0) { + z_free(H, ptr, old_size); + return NULL; + } + if (old_size == 0) return z_malloc(H, new_size); + return z_realloc(H, ptr, old_size, new_size); +} + diff --git a/src/alloc.h b/src/alloc.h new file mode 100644 index 0000000..217605e --- /dev/null +++ b/src/alloc.h @@ -0,0 +1,66 @@ +// Copyright (c) 2024, The paw Authors. All rights reserved. +// This source code is licensed under the MIT License, which can be found in +// LICENSE.md. See AUTHORS.md for a list of contributor names. +// +// alloc: Low-level memory allocation routines +// +// Uses 2 different heuristics depending on allocation size. For small allocations, +// we use a slotted allocator that never attempts to merge freelist entries. For +// large allocations we use a slightly-modified version of the memsys3 allocator from +// SQLite (mem3.c). The large object allocator uses only the hash-based partitioning +// scheme, since it expects only large objects. +// +// TODO: Handle expanding the heap: change paw_Alloc to be more like sbrk, since +// that is all we really need from the user (an initial 'heap' pointer, and a callback +// to expand the original heap allocation if possible). + +#ifndef PAW_ALLOC_H +#define PAW_ALLOC_H + +#include "paw.h" + +struct GcFlag { + uint8_t value; +}; + +#define FAST_BIN_COUNT 1024 + +struct FastBins { + struct BinInfo *info[FAST_BIN_COUNT]; + size_t uninit_size; + size_t arena_size; + void *uninit; + void *arena; +}; + + +struct Heap { + struct FastBins bins; + struct BlockAllocator *a_block; + uintptr_t bounds[2]; + size_t heap_size; + void *heap; + paw_Env *P; + + size_t nflags; + uint8_t flags[]; +}; + +#define Z_IN_BOUNDS(H, u) ((H)->bounds[0] <= (u) && (u) < (H)->bounds[1]) + +int pawZ_init(paw_Env *P, size_t heap_size); +void pawZ_uninit(paw_Env *P); + +void *pawZ_alloc(paw_Env *P, void *ptr, size_t old_size, size_t new_size); + +void pawZ_set_flag(struct Heap *H, uintptr_t ptr); +void pawZ_clear_flag(struct Heap *H, uintptr_t ptr); +uint8_t pawZ_get_flag(struct Heap *H, uintptr_t ptr); + +// Return PAW_TRUE if 'u' points to the start of an Object *, PAW_FALSE otherwise +static inline paw_Bool pawZ_is_object(struct Heap *H, uintptr_t u) +{ + return Z_IN_BOUNDS(H, u) && pawZ_get_flag(H, u); +} + +#endif // PAW_ALLOC_H diff --git a/src/api.c b/src/api.c index ad479d6..02e7604 100644 --- a/src/api.c +++ b/src/api.c @@ -6,7 +6,7 @@ #include "api.h" #include "auxlib.h" #include "call.h" -#include "gc_aux.h" +#include "gc.h" #include "lib.h" #include "map.h" #include "mem.h" @@ -22,19 +22,15 @@ #include -static void *default_alloc(void *ud, void *ptr, size_t size0, size_t size) +static void *default_alloc(void *ud, void *ptr, size_t old_size, size_t new_size) { paw_unused(ud); - if (size0 == 0) { - return GC_MALLOC(size); - // return malloc(size); - } else if (size == 0) { - GC_FREE(ptr); - // free(ptr); + if (new_size == 0) { + free(ptr); return NULL; - } - return GC_REALLOC(ptr, size); - // return realloc(ptr, size); + } + if (old_size == 0) return malloc(new_size); + return realloc(ptr, new_size); } static StackPtr access(paw_Env *P, int index) @@ -44,7 +40,10 @@ static StackPtr access(paw_Env *P, int index) return &P->cf->base.p[i]; } -paw_Alloc paw_get_allocator(paw_Env *P) { return P->alloc; } +paw_Alloc paw_get_allocator(paw_Env *P) +{ + return P->alloc; +} void paw_set_allocator(paw_Env *P, paw_Alloc alloc, void *ud) { @@ -63,21 +62,26 @@ static void open_aux(paw_Env *P, void *arg) pawP_init(P); pawR_init(P); pawL_init(P); - check_gc(P); + CHECK_GC(P); } paw_Env *paw_open(paw_Alloc alloc, void *ud) { alloc = alloc ? alloc : default_alloc; paw_Env *P = alloc(ud, NULL, 0, sizeof *P); - if (!P) { - return NULL; - } + if (P == NULL) return NULL; *P = (paw_Env){ .alloc = alloc, .ud = ud, }; + // TODO: let user pass in options. These options should work for programs that + // don't use a huge amount of memory. + const size_t heap_size = 8388608 * 8; // 64 MiB + if (pawZ_init(P, heap_size)) { + alloc(ud, P, sizeof(*P), 0); + return NULL; + } if (pawC_try(P, open_aux, NULL)) { paw_close(P); return NULL; @@ -89,7 +93,9 @@ void paw_close(paw_Env *P) { pawG_uninit(P); pawC_uninit(P); + pawE_uninit(P); pawS_uninit(P); + pawZ_uninit(P); P->alloc(P->ud, P, sizeof *P, 0); } @@ -287,8 +293,7 @@ int paw_load(paw_Env *P, paw_Reader input, const char *name, void *ud) .ud = ud, }; const int status = pawC_try(P, compile_aux, &p); - pawM_free_vec(P, p.dm.scratch.data, p.dm.scratch.alloc); - pawM_free_vec(P, p.dm.labels.values, p.dm.labels.capacity); + pawP_cleanup(P, &p.dm); return status; } diff --git a/src/ast.c b/src/ast.c index cf2d061..ce7fc13 100644 --- a/src/ast.c +++ b/src/ast.c @@ -9,26 +9,30 @@ #include #include -#define FIRST_ARENA_SIZE 512 +#define FIRST_ARENA_SIZE 4096 #define LARGE_ARENA_MIN 32 struct Ast *pawAst_new(struct Lex *lex) { paw_Env *P = ENV(lex); - struct Ast *tree = pawM_new(P, struct Ast); - tree->lex = lex; - tree->P = P; + struct Ast *ast = pawM_new(P, struct Ast); + *ast = (struct Ast){ + .lex = lex, + .P = P, + }; // initialize memory pools for storing AST components - pawK_pool_init(P, &tree->pool, FIRST_ARENA_SIZE, sizeof(void *) * LARGE_ARENA_MIN); - return tree; + pawK_pool_init(P, &ast->pool, FIRST_ARENA_SIZE, sizeof(void *) * LARGE_ARENA_MIN); + return ast; } void pawAst_free(struct Ast *ast) { - paw_Env *P = ENV(ast); - pawK_pool_uninit(P, &ast->pool); - pawM_free(P, ast); + if (ast != NULL) { + paw_Env *P = ENV(ast); + pawK_pool_uninit(P, &ast->pool); + pawM_free(P, ast); + } } #define DEFINE_NODE_CONSTRUCTOR(name, T) \ @@ -80,7 +84,7 @@ DEFINE_KIND_PRINTER(expr, AstExpr) DEFINE_KIND_PRINTER(decl, AstDecl) DEFINE_KIND_PRINTER(stmt, AstStmt) -#define dump_block(P, b) check_exp(AstIsBlock(AST_CAST_STMT(b)), dump_stmt(P, AST_CAST_STMT(b))) +#define dump_block(P, b) CHECK_EXP(AstIsBlock(AST_CAST_STMT(b)), dump_stmt(P, AST_CAST_STMT(b))) #define dump_name(P, s) DUMP_FMT(P, "name: %s\n", s ? s->text : NULL) static void dump_expr(Printer *P, struct AstExpr *e); @@ -127,7 +131,7 @@ static void dump_decl(Printer *P, struct AstDecl *d) DUMP_FMT(P, "line: %d\n", d->hdr.line); switch (AST_KINDOF(d)) { case kAstFuncDecl: - DUMP_FMT(P, "receiver: %p\n", cast(d->func.receiver, void *)); + DUMP_FMT(P, "receiver: %p\n", CAST(d->func.receiver, void *)); DUMP_FMT(P, "name: %s\n", d->func.name->text); dump_decl_list(P, d->func.generics, "generics"); dump_decl_list(P, d->func.params, "params"); diff --git a/src/ast.h b/src/ast.h index de12fc0..831fb40 100644 --- a/src/ast.h +++ b/src/ast.h @@ -443,9 +443,9 @@ struct AstDecl *pawAst_new_decl(struct Ast *ast, enum AstDeclKind kind); struct AstExpr *pawAst_new_expr(struct Ast *ast, enum AstExprKind kind); struct AstStmt *pawAst_new_stmt(struct Ast *ast, enum AstStmtKind kind); -#define AST_CAST_DECL(x) cast(x, struct AstDecl *) -#define AST_CAST_EXPR(x) cast(x, struct AstExpr *) -#define AST_CAST_STMT(x) cast(x, struct AstStmt *) +#define AST_CAST_DECL(x) CAST(x, struct AstDecl *) +#define AST_CAST_EXPR(x) CAST(x, struct AstExpr *) +#define AST_CAST_STMT(x) CAST(x, struct AstStmt *) DEFINE_LIST(struct Ast, pawAst_decl_list_, AstDeclList, struct AstDecl) DEFINE_LIST(struct Ast, pawAst_expr_list_, AstExprList, struct AstExpr) diff --git a/src/auxlib.c b/src/auxlib.c index 07b7582..d3d2e1f 100644 --- a/src/auxlib.c +++ b/src/auxlib.c @@ -142,7 +142,7 @@ void pawL_add_pointer(paw_Env *P, Buffer *buf, void *p) { char temp[32]; const int n = snprintf(temp, sizeof(temp), "%p", p); - pawL_add_nstring(P, buf, temp, cast_size(n)); + pawL_add_nstring(P, buf, temp, CAST_SIZE(n)); } static const char *add_non_fmt(paw_Env *P, Buffer *buf, const char *ptr) @@ -152,7 +152,7 @@ static const char *add_non_fmt(paw_Env *P, Buffer *buf, const char *ptr) ++p; } if (p != ptr) { - pawL_add_nstring(P, buf, ptr, cast_size(p - ptr)); + pawL_add_nstring(P, buf, ptr, CAST_SIZE(p - ptr)); ptr = p; } return ptr; diff --git a/src/call.c b/src/call.c index bd74cb4..5d632e1 100644 --- a/src/call.c +++ b/src/call.c @@ -16,15 +16,11 @@ #include #include #include -#include #include // Lua-style error handling -#define c_throw(P, c) longjmp((c)->jmp, 1) -#define c_try(P, c, a) \ - if (!setjmp((c)->jmp)) { \ - a \ - } +#define THROW(P, c) longjmp((c)->jmp, 1) +#define TRY(P, c, a) if (!setjmp((c)->jmp)) {a} struct Jump { struct Jump *prev; @@ -34,27 +30,27 @@ struct Jump { static void start_resize(paw_Env *P) { - P->top.d = save_offset(P, P->top.p); - P->bound.d = save_offset(P, P->bound.p); + P->top.d = SAVE_OFFSET(P, P->top.p); + P->bound.d = SAVE_OFFSET(P, P->bound.p); for (CallFrame *cf = P->cf; cf; cf = cf->prev) { - cf->base.d = save_offset(P, cf->base.p); - cf->top.d = save_offset(P, cf->top.p); + cf->base.d = SAVE_OFFSET(P, cf->base.p); + cf->top.d = SAVE_OFFSET(P, cf->top.p); } for (UpValue *up = P->up_list; up; up = up->open.next) { - up->p.d = save_offset(P, upv_level(up)); + up->p.d = SAVE_OFFSET(P, upv_level(up)); } } static void finish_resize(paw_Env *P) { - P->top.p = restore_pointer(P, P->top.d); - P->bound.p = restore_pointer(P, P->bound.d); + P->top.p = RESTORE_POINTER(P, P->top.d); + P->bound.p = RESTORE_POINTER(P, P->bound.d); for (CallFrame *cf = P->cf; cf; cf = cf->prev) { - cf->base.p = restore_pointer(P, cf->base.d); - cf->top.p = restore_pointer(P, cf->top.d); + cf->base.p = RESTORE_POINTER(P, cf->base.d); + cf->top.p = RESTORE_POINTER(P, cf->top.d); } for (UpValue *up = P->up_list; up; up = up->open.next) { - up->p.p = restore_pointer(P, up->p.d); + up->p.p = RESTORE_POINTER(P, up->p.d); } } @@ -66,7 +62,7 @@ void pawC_stack_realloc(paw_Env *P, int n) _Static_assert(PAW_STACK_MAX <= INT_MAX, "stack limit is too large"); paw_assert(n >= pawC_stklen(P)); // don't lose live values - const size_t alloc = paw_max(cast_size(n), STACK_MIN); + const size_t alloc = PAW_MAX(CAST_SIZE(n), STACK_MIN); if (alloc > PAW_STACK_MAX) { pawM_error(P); } @@ -77,16 +73,16 @@ void pawC_stack_realloc(paw_Env *P, int n) // Reallocate the stack. Call one of the low-level allocation functions that // doesn't throw an error. Stack references must be corrected, even if the // allocation fails. - StackPtr stack = - pawM_alloc(P, P->stack.p, sizeof(P->stack.p[0]) * cast_size(P->bound.d), - sizeof(P->stack.p[0]) * alloc); + StackPtr stack = pawM_alloc(P, P->stack.p, + sizeof(P->stack.p[0]) * CAST_SIZE(P->bound.d), + sizeof(P->stack.p[0]) * alloc); P->gc_noem = PAW_FALSE; // allow emergency GC if (!stack) { finish_resize(P); // fix pointers pawM_error(P); // out of memory } // Cause the 'bound' pointer to be placed at the new end of the stack. - P->bound.d = (ptrdiff_t)alloc; + P->bound.d = CAST(alloc, ptrdiff_t); P->stack.p = stack; finish_resize(P); } @@ -101,17 +97,17 @@ void pawC_stack_overflow(paw_Env *P) // the stack to be reallocated. This option also causes pawC_stkdec // to trim the stack each time it is called. #if PAW_STRESS > 1 -#define next_alloc(n0, dn) ((n0) + (dn)) +# define NEXT_ALLOC(n0, dn) ((n0) + (dn)) #else -#define next_alloc(n0, dn) paw_max((n0) + (dn), (n0) * 2) +# define NEXT_ALLOC(n0, dn) PAW_MAX((n0) + (dn), (n0) * 2) #endif void pawC_stack_grow(paw_Env *P, int n) { paw_assert(n > 0); paw_assert(P->bound.p >= P->stack.p); - const int alloc = cast_size(P->bound.p - P->stack.p); - pawC_stack_realloc(P, next_alloc(alloc, n)); + const int alloc = CAST_SIZE(P->bound.p - P->stack.p); + pawC_stack_realloc(P, NEXT_ALLOC(alloc, n)); } Value *pawC_pushns(paw_Env *P, const char *s, size_t n) @@ -160,7 +156,7 @@ static void handle_ccall(paw_Env *P, StackPtr base, Native *ccall) { // The C function may cause the stack to be reallocated. Save the relative // position of 'base' so it can be restored after the call. - const ptrdiff_t pos = save_offset(P, base); + const ptrdiff_t pos = SAVE_OFFSET(P, base); CallFrame *cf = next_call_frame(P, P->top.p); cf->flags = CFF_C; cf->pc = NULL; @@ -170,7 +166,7 @@ static void handle_ccall(paw_Env *P, StackPtr base, Native *ccall) // call the C function const int nret = ccall->func(P); - base = restore_pointer(P, pos); + base = RESTORE_POINTER(P, pos); call_return(P, base, nret); // pawR_close_upvalues(P, base); } @@ -192,9 +188,9 @@ static void check_fixed_args(paw_Env *P, Proto *f, int argc) CallFrame *pawC_precall(paw_Env *P, StackPtr base, Object *callable, int argc) { - const ptrdiff_t offset = save_offset(P, base); - ensure_stack(P, FRAME_EXTRA); - base = restore_pointer(P, offset); + const ptrdiff_t offset = SAVE_OFFSET(P, base); + ENSURE_STACK(P, FRAME_EXTRA); + base = RESTORE_POINTER(P, offset); Native *ccall; Closure *fn = NULL; @@ -205,36 +201,8 @@ CallFrame *pawC_precall(paw_Env *P, StackPtr base, Object *callable, int argc) case VCLOSURE: fn = o_closure(callable); break; - // case VMETHOD: { - // Method *mtd = v_method(callable); - // *base = mtd->self; // replace with self - // if (pawV_is_native(mtd->f)) { - // ccall = v_native(mtd->f); - // goto call_native; - // } else { - // assert(pawV_is_closure(mtd->f)); - // fn = v_closure(mtd->f); - // } - // break; - // } - // case VCLASS: { - // Class *cls = v_class(callable); - // pawV_new_instance(P, base, cls); - // Value name = pawE_cstr(P, CSTR_INIT); // '__init' - // Value *init = pawH_get(P, cls->attr, name); - // if (!init) { - // // There is no user-defined initializer, so just - // return - // // the instance. - // P->top.p = base + 1; - // return P->cf; - // } - // fn = v_closure(*init); - // break; - // } default: - paw_assert(0); - // pawR_error(P, PAW_ETYPE, "type is not callable"); + pawR_error(P, PAW_ETYPE, "type is not callable"); } CallFrame *cf = next_call_frame(P, P->top.p); Proto *p = fn->p; @@ -271,7 +239,7 @@ static int exceptional_call(paw_Env *P, Call call, void *arg) .prev = P->jmp, }; P->jmp = &jmp; - c_try(P, &jmp, call(P, arg);); + TRY(P, &jmp, call(P, arg);) P->jmp = jmp.prev; return jmp.status; } @@ -279,20 +247,16 @@ static int exceptional_call(paw_Env *P, Call call, void *arg) int pawC_try(paw_Env *P, Call call, void *arg) { CallFrame *cf = P->cf; - const ptrdiff_t top = save_offset(P, P->top.p); + const ptrdiff_t top = SAVE_OFFSET(P, P->top.p); const int status = exceptional_call(P, call, arg); if (status != PAW_OK) { - paw_assert(top <= save_offset(P, P->top.p)); - StackPtr ptr = restore_pointer(P, top); + paw_assert(top <= SAVE_OFFSET(P, P->top.p)); + StackPtr ptr = RESTORE_POINTER(P, top); pawR_close_upvalues(P, ptr); if (cf == &P->main) { // relocate the error message call_return(P, ptr, PAW_TRUE); } else { - // TODO: The error message gets ignored so that the error status - // can be returned in paw (no multi-return). Once that gets - // implemented, the error message can be the second return - // value. P->top.p = ptr; } P->cf = cf; @@ -306,7 +270,7 @@ _Noreturn void pawC_throw(paw_Env *P, int error) abort(); } P->jmp->status = error; - c_throw(P, P->jmp); + THROW(P, P->jmp); } void pawC_init(paw_Env *P) diff --git a/src/call.h b/src/call.h index 0df3bb7..ada8bb8 100644 --- a/src/call.h +++ b/src/call.h @@ -9,11 +9,11 @@ #include "value.h" #define STACK_EXTRA 1 /* number of slots reserved for errors */ -#define save_offset(P, ptr) ((ptr) - (P)->stack.p) -#define restore_pointer(P, ofs) ((P)->stack.p + (ofs)) -#define ensure_stack(P, n) \ +#define SAVE_OFFSET(P, ptr) ((ptr) - (P)->stack.p) +#define RESTORE_POINTER(P, ofs) ((P)->stack.p + (ofs)) +#define ENSURE_STACK(P, n) \ ((P)->bound.p - (P)->top.p < (n) + STACK_EXTRA \ - ? pawC_stack_grow(P, n + STACK_EXTRA) \ + ? pawC_stack_grow(P, n + STACK_EXTRA) \ : (void)0) typedef void (*Call)(paw_Env *P, void *arg); @@ -30,14 +30,17 @@ void pawC_stack_grow(paw_Env *P, int count); void pawC_stack_realloc(paw_Env *P, int n); void pawC_stack_overflow(paw_Env *P); -static inline int pawC_stklen(paw_Env *P) { return P->top.p - P->stack.p; } +static inline int pawC_stklen(paw_Env *P) +{ + return P->top.p - P->stack.p; +} // Increase the stack size // New slots have unspecified values and must be set before the next // collection runs. static inline StackPtr pawC_stkinc(paw_Env *P, int n) { - ensure_stack(P, n); + ENSURE_STACK(P, n); StackPtr sp = P->top.p; P->top.p += n; return sp; @@ -99,6 +102,9 @@ static inline Value *pawC_pusho(paw_Env *P, Object *o) Value *pawC_pushns(paw_Env *P, const char *s, size_t n); Value *pawC_pushs(paw_Env *P, const char *s); -static inline void pawC_pop(paw_Env *P) { pawC_stkdec(P, 1); } +static inline void pawC_pop(paw_Env *P) +{ + pawC_stkdec(P, 1); +} #endif // PAW_CALL_H diff --git a/src/code.c b/src/code.c index 364815e..94f454a 100644 --- a/src/code.c +++ b/src/code.c @@ -118,8 +118,8 @@ void pawK_pool_uninit(paw_Env *P, struct Pool *pool) static void *find_free_block(struct Pool *pool, size_t size) { for (struct FreeBlock **p = &pool->free; *p; p = &(*p)->prev) { - if (size <= cast_size((*p)->size)) { - paw_assert(0 == (cast(*p, uintptr_t) & (K_ALIGNOF_NODE - 1))); + if (size <= CAST_SIZE((*p)->size)) { + paw_assert(0 == (CAST_UPTR(*p) & (K_ALIGNOF_NODE - 1))); struct FreeBlock *block = *p; *p = (*p)->prev; return block; @@ -154,7 +154,7 @@ void *pawK_pool_alloc(paw_Env *P, struct Pool *pool, size_t size) void pawK_pool_free(struct Pool *pool, void *ptr, size_t size) { -// paw_assert(0 == (cast(ptr, uintptr_t) & _Alignof(struct FreeBlock))); +// paw_assert(0 == (CAST_UPTR(ptr) & _Alignof(struct FreeBlock))); // paw_assert(size >= sizeof(struct FreeBlock)); // const struct FreeBlock prototype = { // .prev = pool->free, diff --git a/src/code.h b/src/code.h index f62707b..2531e5d 100644 --- a/src/code.h +++ b/src/code.h @@ -47,7 +47,7 @@ void pawK_pool_free(struct Pool *pool, void *ptr, size_t size); static inline struct L *func##new(ctx *X) \ { \ pawM_check_size(X->P, 0, K_LIST_MIN, sizeof(void *)); \ - const size_t size = cast_size(K_LIST_MIN) * sizeof(void *); \ + const size_t size = CAST_SIZE(K_LIST_MIN) * sizeof(void *); \ struct L *list = pawK_pool_alloc(X->P, &X->pool, sizeof(struct L)); \ list->data = pawK_pool_alloc(X->P, &X->pool, size); \ list->alloc = K_LIST_MIN; \ @@ -60,10 +60,10 @@ void pawK_pool_free(struct Pool *pool, void *ptr, size_t size); pawM_error(X->P); \ } \ const size_t elemsz = sizeof(list->data[0]); \ - const size_t nextcap = cast_size(list->alloc) * 2; \ + const size_t nextcap = CAST_SIZE(list->alloc) * 2; \ const size_t bufsz = nextcap * elemsz; \ void *next = pawK_pool_alloc(X->P, &X->pool, bufsz); \ - const size_t usedsz = cast_size(list->count) * elemsz; \ + const size_t usedsz = CAST_SIZE(list->count) * elemsz; \ memcpy(next, list->data, usedsz); \ list->alloc = nextcap; \ list->data = next; \ diff --git a/src/codegen.c b/src/codegen.c index c658024..4928141 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -3,7 +3,7 @@ // LICENSE.md. See AUTHORS.md for a list of contributor names. #include "code.h" -#include "gc_aux.h" +#include "gc.h" #include "hir.h" #include "map.h" #include "mem.h" @@ -94,8 +94,11 @@ static String *mangle_name(struct Generator *G, const String *name, struct HirTy } pawL_add_char(P, &buf, '_'); pawL_push_result(P, &buf); + // anchor in compiler string table String *result = v_string(P->top.p[-1]); - pawC_pop(P); // unanchor + const Value key = {.o = CAST_OBJECT(result)}; + pawH_insert(P, G->C->strings, key, key); + pawC_pop(P); return result; } @@ -124,8 +127,12 @@ static int add_proto(struct Generator *G, Proto *proto) Proto *p = fs->proto; if (fs->nproto == ITEM_MAX) { SYNTAX_ERROR(G, "too many functions"); + } else if (fs->nproto == p->nproto) { + pawM_grow(ENV(G), p->p, fs->nproto, p->nproto); + for (int i = fs->nproto; i < p->nproto; ++i) { + p->p[i] = NULL; + } } - pawM_grow(ENV(G), p->p, fs->nproto, p->nproto); const int id = fs->nproto++; p->p[id] = proto; return id; @@ -148,30 +155,36 @@ static void pop_proto(struct Generator *G) pawC_pop(ENV(G)); } +static struct LocalSlot *get_local_slot(struct FuncState *fs, int index) +{ + return &fs->G->C->dm->vars.data[fs->first_local + index]; +} + static paw_Bool needs_close(struct FuncState *fs, const struct BlockState *bs) { - for (int i = fs->level - 1; i >= bs->level; --i) { - if (fs->locals.slots[i].is_captured) { - return PAW_TRUE; - } + struct DynamicMem *dm = fs->G->C->dm; + for (int i = fs->nlocals - 1; i >= bs->level; --i) { + const struct LocalSlot *slot = get_local_slot(fs, i); + if (slot->is_captured) return PAW_TRUE; } return PAW_FALSE; } static void close_vars(struct FuncState *fs, const struct BlockState *bs) { - if (fs->level > bs->level) { + if (fs->nlocals > bs->level) { const Op op = needs_close(fs, bs) ? OP_CLOSE : OP_POP; - pawK_code_U(fs, op, fs->level - bs->level); + pawK_code_U(fs, op, fs->nlocals - bs->level); } } static struct HirVarInfo declare_local(struct FuncState *fs, String *name, struct HirType *type) { struct Generator *G = fs->G; - pawM_grow(ENV(G), fs->locals.slots, fs->locals.nslots, fs->locals.capacity); - const int index = fs->locals.nslots++; - fs->locals.slots[index] = (struct LocalSlot){ + struct DynamicMem *dm = G->C->dm; + pawM_grow(ENV(G), dm->vars.data, dm->vars.count, dm->vars.alloc); + const int index = dm->vars.count++; + dm->vars.data[index] = (struct LocalSlot){ .type = type, .name = name, .index = index, @@ -195,13 +208,14 @@ static struct HirVarInfo new_global(struct Generator *G, String *name, struct Hi static void begin_local_scope(struct FuncState *fs, int n) { - fs->level += n; + fs->nlocals += n; } static void end_local_scope(struct FuncState *fs, struct BlockState *bs) { - fs->locals.nslots = bs->level; - fs->level = bs->level; + struct DynamicMem *dm = fs->G->C->dm; + dm->vars.count = fs->first_local + bs->level; + fs->nlocals = bs->level; } static void define_local(struct FuncState *fs) @@ -268,7 +282,7 @@ static void add_label(struct FuncState *fs, enum LabelKind kind) ll->values[ll->length] = (struct Label){ .kind = kind, .line = -1, // TODO: Get from HirLabelStmt - .level = fs->level - fs->bs->level, + .level = fs->nlocals - fs->bs->level, .pc = code_jump(fs, OP_JUMP), }; ++ll->length; @@ -344,7 +358,7 @@ static void leave_block(struct FuncState *fs) static void enter_block(struct FuncState *fs, struct BlockState *bs, paw_Bool loop) { bs->label0 = fs->G->C->dm->labels.length; - bs->level = fs->level; + bs->level = fs->nlocals; bs->is_loop = loop; bs->outer = fs->bs; fs->bs = bs; @@ -358,7 +372,7 @@ static void leave_function(struct Generator *G) // end function-scoped locals end_local_scope(fs, bs); - paw_assert(fs->level == 0); + paw_assert(fs->nlocals == 0); paw_assert(bs->outer == NULL); // TODO: Need a return at the end to handle cleaning up the stack @@ -378,30 +392,25 @@ static void leave_function(struct Generator *G) p->nk = fs->nk; G->fs = fs->outer; - check_gc(ENV(G)); + CHECK_GC(ENV(G)); } static void enter_function(struct Generator *G, struct FuncState *fs, struct BlockState *bs, - struct HirType *type, enum FuncKind kind) -{ - fs->bs = NULL; - fs->scopes = pawHir_new_symtab(G->hir); - fs->locals = (struct LocalStack){0}; - fs->nproto = 0; - fs->nlines = 0; - fs->level = 0; - fs->nup = 0; - fs->nk = 0; - fs->pc = 0; - - fs->kind = kind; - fs->outer = G->fs; - fs->G = G; + String *name, Proto *proto, struct HirType *type, enum FuncKind kind) +{ + *fs = (struct FuncState){ + .first_local = G->C->dm->vars.count, + .scopes = pawHir_new_symtab(G->hir), + .proto = proto, + .outer = G->fs, + .name = name, + .kind = kind, + .G = G, + }; G->fs = fs; - // Enter the function body. + // enter the function body enter_block(fs, bs, PAW_FALSE); - new_local(fs, fs->name, type); } @@ -417,9 +426,10 @@ static paw_Bool resolve_global(struct Generator *G, const String *name, struct H static paw_Bool resolve_local(struct FuncState *fs, const String *name, struct HirVarInfo *pinfo) { - for (int i = fs->level - 1; i >= 0; --i) { - struct LocalSlot slot = fs->locals.slots[i]; - if (pawS_eq(slot.name, name)) { + struct DynamicMem *dm = fs->G->C->dm; + for (int i = fs->nlocals - 1; i >= 0; --i) { + struct LocalSlot *slot = get_local_slot(fs, i); + if (pawS_eq(slot->name, name)) { pinfo->kind = VAR_LOCAL; pinfo->index = i; return PAW_TRUE; @@ -470,12 +480,12 @@ static void add_upvalue(struct FuncState *fs, struct HirVarInfo *info, paw_Bool static paw_Bool resolve_upvalue(struct FuncState *fs, const String *name, struct HirVarInfo *pinfo) { + struct DynamicMem *dm = fs->G->C->dm; struct FuncState *caller = fs->outer; - if (!caller) { - return PAW_FALSE; - } + if (caller == NULL) return PAW_FALSE; if (resolve_local(caller, name, pinfo)) { - caller->locals.slots[pinfo->index].is_captured = PAW_TRUE; + get_local_slot(caller, pinfo->index) + ->is_captured = PAW_TRUE; add_upvalue(fs, pinfo, PAW_TRUE); return PAW_TRUE; } @@ -499,7 +509,7 @@ static struct HirVarInfo find_var(struct Generator *G, const String *name) } #define CODE_OP(fs, op, subop, type) \ - pawK_code_AB(fs, op, cast(subop, int), basic_code((fs)->G, type)) + pawK_code_AB(fs, op, CAST(subop, int), basic_code((fs)->G, type)) // TODO: OP_PUSHFALSE is a hack to avoid creating unnecessary constants, // essentially pushes integer 0 @@ -770,12 +780,11 @@ static void code_closure_expr(struct HirVisitor *V, struct HirClosureExpr *e) struct FuncState fs; struct BlockState bs; struct Generator *G = V->ud; - fs.name = SCAN_STRING(G->C, "(closure)"); - fs.G = G; + String *name = SCAN_STRING(G->C, "(closure)"); + Proto *proto = push_proto(G, name); + proto->argc = e->params->count; - fs.proto = push_proto(G, fs.name); - fs.proto->argc = e->params->count; - enter_function(G, &fs, &bs, e->type, FUNC_CLOSURE); + enter_function(G, &fs, &bs, name, proto, e->type, FUNC_CLOSURE); V->VisitDeclList(V, e->params); if (e->has_body) { V->VisitBlock(V, e->body); @@ -805,13 +814,11 @@ static void code_func(struct HirVisitor *V, struct HirFuncDecl *d, struct HirVar struct Generator *G = V->ud; struct FuncState fs; struct BlockState bs; - fs.name = d->name; - fs.G = G; - struct HirFuncDef *func = &d->type->fdef; - fs.proto = push_proto(G, d->name); - fs.proto->argc = func->params->count; - enter_function(G, &fs, &bs, d->type, d->fn_kind); + Proto *proto = push_proto(G, d->name); + proto->argc = func->params->count; + + enter_function(G, &fs, &bs, d->name, proto, d->type, d->fn_kind); V->VisitDeclList(V, d->params); // code parameters V->VisitBlock(V, d->body); // code function body leave_function(G); @@ -1238,7 +1245,7 @@ static void add_builtin_func(struct Generator *G, const char *name) String *str = SCAN_STRING(G->C, name); const int g = pawE_new_global(P, str); GlobalVar *gv = pawE_get_global(P, g); - const Value key = {.o = cast_object(str)}; + const Value key = {.o = CAST_OBJECT(str)}; const Value *pv = pawH_get_(P->builtin, key); gv->value = *pv; } @@ -1280,24 +1287,12 @@ static void setup_pass(struct HirVisitor *V, struct Generator *G) add_builtin_func(G, "_vector_clone"); } -static void code_module(struct Generator *G) +static void code_module(struct Generator *G, struct Hir *hir) { - struct Hir *hir = G->hir; - - struct FuncState fs; - struct BlockState bs; - fs.name = G->C->modname; - fs.proto = G->C->main->p; - - enter_function(G, &fs, &bs, NULL, FUNC_MODULE); - struct HirVisitor V; setup_pass(&V, G); - register_items(G, hir->items); code_items(&V); - - leave_function(G); } void p_codegen(struct Compiler *C, struct Hir *hir) @@ -1310,5 +1305,5 @@ void p_codegen(struct Compiler *C, struct Hir *hir) .P = ENV(C), .C = C, }; - code_module(&G); + code_module(&G, hir); } diff --git a/src/compile.c b/src/compile.c index 3bbf62d..8441226 100644 --- a/src/compile.c +++ b/src/compile.c @@ -5,7 +5,7 @@ #include "compile.h" #include "ast.h" #include "call.h" -#include "gc_aux.h" +#include "gc.h" #include "hir.h" #include "map.h" #include "parse.h" @@ -32,7 +32,7 @@ String *pawP_scan_nstring(paw_Env *P, Map *st, const char *s, size_t n) Value *value = pawH_action(P, st, *pv, MAP_ACTION_CREATE); *value = *pv; // anchor in map pawC_pop(P); - check_gc(P); + CHECK_GC(P); return v_string(*value); } @@ -82,3 +82,13 @@ Closure *pawP_compile(paw_Env *P, paw_Reader input, struct DynamicMem *dm, const pawC_stkdec(P, 1); return C.main; } + +void pawP_cleanup(paw_Env *P, const struct DynamicMem *dm) +{ + pawM_free_vec(P, dm->labels.values, dm->labels.capacity); + pawM_free_vec(P, dm->scratch.data, dm->scratch.alloc); + pawM_free_vec(P, dm->decls.data, dm->decls.alloc); + pawM_free_vec(P, dm->vars.data, dm->vars.alloc); + pawAst_free(dm->ast); + pawHir_free(dm->hir); +} diff --git a/src/compile.h b/src/compile.h index d84d433..20ac035 100644 --- a/src/compile.h +++ b/src/compile.h @@ -21,6 +21,7 @@ Closure *pawP_compile(paw_Env *P, paw_Reader input, struct DynamicMem *dm, const char *name, void *ud); +void pawP_cleanup(paw_Env *P, const struct DynamicMem *dm); String *pawP_scan_nstring(paw_Env *P, Map *st, const char *s, size_t n); static inline String *pawP_scan_string(paw_Env *P, Map *st, const char *s) diff --git a/src/config.h b/src/config.h index 86205c2..abf6599 100644 --- a/src/config.h +++ b/src/config.h @@ -14,22 +14,26 @@ #define paw_cast_int(x) ((paw_Int)(x)) #define paw_int_c(x) INT64_C(x) +#ifndef PAW_ALIGN +# define PAW_ALIGN 8 +#endif + #ifndef PAW_NAME_MAX -#define PAW_NAME_MAX 128 +# define PAW_NAME_MAX 128 #endif #ifndef PAW_STACK_MAX -#define PAW_STACK_MAX 1000000 +# define PAW_STACK_MAX 1000000 #endif #if defined(__APPLE__) -#define PAW_OS_MACOS -#define PAW_OS_POSIX +# define PAW_OS_MACOS +# define PAW_OS_POSIX #elif defined(__linux__) -#define PAW_OS_LINUX -#define PAW_OS_POSIX +# define PAW_OS_LINUX +# define PAW_OS_POSIX #elif defined(_WIN32) -#define PAW_OS_WINDOWS +# define PAW_OS_WINDOWS #endif #if defined(__GNUC__) || defined(__clang__) diff --git a/src/ctx.h b/src/ctx.h deleted file mode 100644 index e69de29..0000000 diff --git a/src/env.c b/src/env.c index dbbb336..074b855 100644 --- a/src/env.c +++ b/src/env.c @@ -22,6 +22,12 @@ void pawE_error(paw_Env *P, int code, int line, const char *fmt, ...) pawC_throw(P, code); } +void pawE_uninit(paw_Env *P) +{ + struct GlobalVec *gv = &P->gv; + pawM_free_vec(P, gv->data, gv->alloc); +} + CallFrame *pawE_extend_cf(paw_Env *P, StackPtr top) { if (P->ncf >= ITEM_MAX) { @@ -38,7 +44,7 @@ CallFrame *pawE_extend_cf(paw_Env *P, StackPtr top) int pawE_new_global(paw_Env *P, String *name) { - struct GlobalVec *gv = &P->gv; // enforce uniqueness + struct GlobalVec *gv = &P->gv; for (int i = 0; i < gv->size; ++i) { if (pawS_eq(name, gv->data[i].name)) { pawE_error(P, PAW_ENAME, -1, "duplicate global '%s'", name->text); @@ -62,4 +68,4 @@ int pawE_find_global(paw_Env *P, const String *name) } } return -1; -} +} diff --git a/src/env.h b/src/env.h index c67f6f1..fa779be 100644 --- a/src/env.h +++ b/src/env.h @@ -1,10 +1,10 @@ // Copyright (c) 2024, The paw Authors. All rights reserved. // This source code is licensed under the MIT License, which can be found in // LICENSE.md. See AUTHORS.md for a list of contributor names. -#ifndef PAW_CONTEXT_H -#define PAW_CONTEXT_H +#ifndef PAW_ENV_H +#define PAW_ENV_H -#include "meta.h" +#include "alloc.h" #include "opcode.h" #include "paw.h" #include "str.h" @@ -16,15 +16,6 @@ struct Jump; // call.c -typedef struct Module { - Struct **structs; - Enum **enums; - Value *slots; - int nstructs; - int nenums; - int nslots; -} Module; - #define CFF_C 1 #define CFF_ENTRY 2 @@ -80,7 +71,7 @@ struct MethodList { Value data[]; }; -typedef struct paw_Env { +typedef struct paw_Env { paw_Bool done; // TODO: remove StringTable strings; CallFrame main; @@ -97,7 +88,6 @@ typedef struct paw_Env { String *modname; Map *builtin; Map *libs; - Module *mod; Value meta_keys[NMETAMETHODS]; // Array of commonly-used strings. @@ -113,6 +103,7 @@ typedef struct paw_Env { int alloc; } gv; + struct Heap *H; paw_Alloc alloc; void *ud; @@ -124,6 +115,7 @@ typedef struct paw_Env { paw_Bool gc_noem; } paw_Env; +void pawE_uninit(paw_Env *P); void pawE_error(paw_Env *P, int code, int line, const char *fmt, ...); CallFrame *pawE_extend_cf(paw_Env *P, StackPtr top); int pawE_new_global(paw_Env *P, String *name); @@ -136,4 +128,4 @@ static inline String *pawE_cstr(paw_Env *P, unsigned type) return P->str_cache[type]; } -#endif // PAW_CONTEXT_H +#endif // PAW_ENV_H diff --git a/src/gc.c b/src/gc.c new file mode 100644 index 0000000..4392b2c --- /dev/null +++ b/src/gc.c @@ -0,0 +1,375 @@ +// Copyright (c) 2024, The paw Authors. All rights reserved. +// This source code is licensed under the MIT License, which can be found in +// LICENSE.md. See AUTHORS.md for a list of contributor names. + +#include "gc.h" +#include "env.h" +#include "map.h" +#include "mem.h" +#include "util.h" +#include +#include +#include +#include + +#ifndef PAW_GC_LIMIT +#define PAW_GC_LIMIT (1024 * 1024) +#endif + +static void gc_trace_object(const char *msg, void *ptr) +{ +#ifdef PAW_TRACE_GC + fprintf(stdout, "(gc) %s: %p\n", msg, ptr); +#else + paw_unused(msg); + paw_unused(ptr); +#endif +} + +enum { + GC_WHITE, + GC_GRAY, + GC_BLACK, +}; + +#define SET_MARK(x, m) ((x)->gc_mark = (m)) +#define SET_WHITE(x) SET_MARK(x, GC_WHITE) +#define SET_GRAY(x) SET_MARK(x, GC_GRAY) +#define SET_BLACK(x) SET_MARK(x, GC_BLACK) +#define IS_WHITE(x) ((x)->gc_mark == GC_WHITE) +#define IS_GRAY(x) ((x)->gc_mark == GC_GRAY) +#define IS_BLACK(x) ((x)->gc_mark == GC_BLACK) + +static Object **get_gc_list(Object *o) +{ + switch (o->gc_kind) { + case VFOREIGN: + return &o_foreign(o)->gc_list; + case VVECTOR: + return &o_vector(o)->gc_list; + case VMAP: + return &o_map(o)->gc_list; + case VCLOSURE: + return &o_closure(o)->gc_list; + case VPROTO: + return &o_proto(o)->gc_list; + case VINSTANCE: + return &o_instance(o)->gc_list; + case VVARIANT: + return &o_variant(o)->gc_list; + case VTUPLE: + return &o_tuple(o)->gc_list; + case VNATIVE: + return &o_native(o)->gc_list; + default: + paw_assert(o->gc_kind == VMETHOD); + return &(o_method(o))->gc_list; + } +} + +static void link_gray_(Object *o, Object **pnext, Object **list) +{ + if (!IS_GRAY(o)) { + SET_GRAY(o); + *pnext = *list; + *list = o; + } +} + +#define LINK_GRAY(o, L) link_gray_(o, get_gc_list(o), &(L)) + +static void mark_value(paw_Env *P, Value v); + +static void mark_object(paw_Env *P, Object *o) +{ + if (!o || !IS_WHITE(o)) return; + gc_trace_object("mark", o); + switch (o->gc_kind) { + case VUPVALUE: { + UpValue *u = o_upvalue(o); + mark_value(P, *u->p.p); + SET_BLACK(u); + break; + } + case VSTRING: + SET_BLACK(o); + break; + default: + LINK_GRAY(o, P->gc_gray); + } +} + +static void mark_value(paw_Env *P, Value v) +{ + if (pawZ_is_object(P->H, v.u)) { + mark_object(P, v_object(v)); + } +} + +static void traverse_proto(paw_Env *P, Proto *p) +{ + mark_object(P, CAST_OBJECT(p->name)); + mark_object(P, CAST_OBJECT(p->modname)); + for (int i = 0; i < p->nproto; ++i) { + mark_object(P, CAST_OBJECT(p->p[i])); + } + for (int i = 0; i < p->nk; ++i) { + mark_value(P, p->k[i]); + } +} + +static void traverse_closure(paw_Env *P, Closure *f) +{ + mark_object(P, CAST_OBJECT(f->p)); + for (int i = 0; i < f->nup; ++i) { + mark_object(P, CAST_OBJECT(f->up[i])); + } +} + +static void traverse_native(paw_Env *P, Native *f) +{ + for (int i = 0; i < f->nup; ++i) { + mark_value(P, f->up[i]); + } +} + +static void traverse_fields(paw_Env *P, Value *pv, int n) +{ + for (int i = 0; i < n; ++i) { + mark_value(P, pv[i]); + } +} + +static void traverse_tuple(paw_Env *P, Tuple *t) +{ + traverse_fields(P, t->elems, t->nelems); +} + +static void traverse_instance(paw_Env *P, Instance *i) +{ + traverse_fields(P, i->attrs, i->nfields); +} + +static void traverse_method(paw_Env *P, Method *m) +{ + mark_object(P, v_object(m->self)); + mark_object(P, v_object(m->f)); +} + +static void traverse_vector(paw_Env *P, Vector *a) +{ + paw_Int itr = PAW_ITER_INIT; + while (pawV_vec_iter(a, &itr)) { + mark_value(P, *pawV_vec_get(P, a, itr)); + } +} + +static void traverse_map(paw_Env *P, Map *m) +{ + paw_Int itr = PAW_ITER_INIT; + while (pawH_iter(m, &itr)) { + mark_value(P, *pawH_key(m, CAST_SIZE(itr))); + mark_value(P, *pawH_value(m, CAST_SIZE(itr))); + } +} + +static void traverse_variant(paw_Env *P, Variant *v) +{ + traverse_fields(P, v->fields, v->nfields); +} + +static void traverse_foreign(paw_Env *P, Foreign *u) +{ + traverse_fields(P, u->attrs, u->nfields); +} + +static void mark_roots(paw_Env *P) +{ + for (StackPtr p = P->stack.p; p != P->top.p; ++p) { + mark_value(P, *p); + } + for (CallFrame *cf = P->cf; cf; cf = cf->prev) { + mark_object(P, CAST_OBJECT(cf->fn)); + } + for (UpValue *u = P->up_list; u; u = u->open.next) { + mark_object(P, CAST_OBJECT(u)); + } + for (int i = 0; i < P->gv.size; ++i) { + mark_object(P, CAST_OBJECT(P->gv.data[i].name)); + mark_value(P, P->gv.data[i].value); + } + mark_object(P, CAST_OBJECT(P->builtin)); + mark_object(P, CAST_OBJECT(P->libs)); +} + +static void traverse_objects(paw_Env *P) +{ + for (Object **po = &P->gc_gray; *po;) { + Object *o = *po; + + Object **plist = get_gc_list(o); + *po = *plist; + *plist = NULL; + SET_BLACK(o); + + gc_trace_object("traverse", o); + switch (o->gc_kind) { + case VCLOSURE: + traverse_closure(P, o_closure(o)); + break; + case VPROTO: + traverse_proto(P, o_proto(o)); + break; + case VNATIVE: + traverse_native(P, o_native(o)); + break; + case VTUPLE: + traverse_tuple(P, o_tuple(o)); + break; + case VINSTANCE: + traverse_instance(P, o_instance(o)); + break; + case VMETHOD: + traverse_method(P, o_method(o)); + break; + case VVECTOR: + traverse_vector(P, o_vector(o)); + break; + case VMAP: + traverse_map(P, o_map(o)); + break; + case VFOREIGN: + traverse_foreign(P, o_foreign(o)); + break; + default: + traverse_variant(P, o_variant(o)); + } + } +} + +static void mark_phase(paw_Env *P) +{ + mark_roots(P); + traverse_objects(P); +} + +static void sweep_phase(paw_Env *P) +{ + for (Object **p = &P->gc_all; *p;) { + Object *o = *p; + if (IS_WHITE(o)) { + *p = o->gc_next; + pawG_free_object(P, o); + } else { + p = &o->gc_next; + paw_assert(IS_BLACK(o)); + SET_WHITE(o); + } + } +} + +void pawS_check(paw_Env*P); +void pawG_collect(paw_Env *P) +{ + P->gc_gray = NULL; + + mark_phase(P); + sweep_phase(P); + + P->gc_limit = P->gc_bytes * 2; +} + +void pawG_add_object(paw_Env *P, Object *o, ValueKind kind) +{ + gc_trace_object("register", o); + pawZ_set_flag(P->H, CAST_UPTR(o)); + o->gc_kind = kind; + o->gc_next = P->gc_all; + P->gc_all = o; +} + +void pawG_init(paw_Env *P) +{ + P->gc_limit = PAW_GC_LIMIT; +} + +void pawG_uninit(paw_Env *P) +{ + P->libs = NULL; + P->builtin = NULL; + P->up_list = NULL; + P->top = P->stack; + P->gv.size = 0; + + pawG_collect(P); + + for (CallFrame *cf = P->main.next; cf;) { + CallFrame *next = cf->next; + pawM_free(P, cf); + cf = next; + } + P->cf = NULL; + P->ncf = 0; + + for (Object *o = P->gc_fixed; o;) { + Object *next = o->gc_next; + pawG_free_object(P, o); + o = next; + } + P->gc_fixed = NULL; +} + +void pawG_fix_object(paw_Env *P, Object *o) +{ + paw_assert(P->gc_all == o); + paw_assert(IS_WHITE(o)); + + SET_GRAY(o); + P->gc_all = o->gc_next; + o->gc_next = P->gc_fixed; + P->gc_fixed = o; +} + +void pawG_free_object(paw_Env *P, Object *o) +{ + pawZ_clear_flag(P->H, CAST_UPTR(o)); + switch (o->gc_kind) { + case VUPVALUE: + pawV_free_upvalue(P, o_upvalue(o)); + break; + case VCLOSURE: + pawV_free_closure(P, o_closure(o)); + break; + case VFOREIGN: + pawV_free_foreign(P, o_foreign(o)); + break; + case VSTRING: + pawS_free_str(P, o_string(o)); + break; + case VMAP: + pawH_free(P, o_map(o)); + break; + case VVECTOR: + pawV_vec_free(P, o_vector(o)); + break; + case VPROTO: + pawV_free_proto(P, o_proto(o)); + break; + case VINSTANCE: + pawV_free_instance(P, o_instance(o)); + break; + case VMETHOD: + pawV_free_method(P, o_method(o)); + break; + case VNATIVE: + pawV_free_native(P, o_native(o)); + break; + case VVARIANT: + pawV_free_variant(P, o_variant(o)); + break; + default: + pawV_free_tuple(P, o_tuple(o)); + break; + } +} + diff --git a/src/gc_aux.h b/src/gc.h similarity index 76% rename from src/gc_aux.h rename to src/gc.h index ee70935..7e1c578 100644 --- a/src/gc_aux.h +++ b/src/gc.h @@ -7,11 +7,10 @@ #include "paw.h" #include "value.h" -#define check_gc(P) \ - do { \ +#define CHECK_GC(P) do { \ if ((P)->gc_bytes > (P)->gc_limit) { \ - pawG_collect(P); \ - } \ + pawG_collect(P); \ + } \ } while (0) void pawG_init(paw_Env *P); diff --git a/src/gc_aux.c b/src/gc_aux.c deleted file mode 100644 index 6d5aa0c..0000000 --- a/src/gc_aux.c +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright (c) 2024, The paw Authors. All rights reserved. -// This source code is licensed under the MIT License, which can be found in -// LICENSE.md. See AUTHORS.md for a list of contributor names. -#include "gc_aux.h" -#include "env.h" -#include "map.h" -#include "mem.h" -#include "util.h" -#include -#include -#include -#include - -// TODO: Using Boehm-Demers-Weiser GC for now -#include - -void pawG_init(paw_Env *P) { paw_unused(P); } - -void pawG_uninit(paw_Env *P) { paw_unused(P); } - -void pawG_collect(paw_Env *P) -{ - paw_unused(P); - P->gc_bytes = 0; -} - -void pawG_fix_object(paw_Env *P, Object *o) -{ - paw_unused(P); - paw_unused(o); -} - -void pawG_add_object(paw_Env *P, Object *o, ValueKind kind) -{ - paw_unused(P); - o->gc_kind = kind; -} - -void pawG_free_object(paw_Env *P, Object *o) -{ - paw_unused(P); - paw_unused(o); -} - -#if 0 -#ifndef PAW_GC_LIMIT -#define PAW_GC_LIMIT (1024 * 1024) -#endif - -static void gc_trace_object(const char *msg, void *ptr) -{ -#ifdef PAW_TRACE_GC - fprintf(stdout, "(gc) %s: %p\n", msg, ptr); -#else - paw_unused(msg); - paw_unused(ptr); -#endif -} - -enum { - GC_WHITE, - GC_GRAY, - GC_BLACK, -}; - -#define set_mark(x, m) ((x)->gc_mark = (m)) -#define set_white(x) set_mark(x, GC_WHITE) -#define set_gray(x) set_mark(x, GC_GRAY) -#define set_black(x) set_mark(x, GC_BLACK) -#define is_white(x) ((x)->gc_mark == GC_WHITE) -#define is_gray(x) ((x)->gc_mark == GC_GRAY) -#define is_black(x) ((x)->gc_mark == GC_BLACK) - -static Object **get_gc_list(Object *o) -{ -// switch (o->gc_kind) { -// case VFOREIGN: -// return &o_foreign(o)->gc_list; -// case VARRAY: -// return &o_array(o)->gc_list; -// case VMAP: -// return &o_map(o)->gc_list; -// case VCLOSURE: -// return &o_closure(o)->gc_list; -// case VPROTO: -// return &o_proto(o)->gc_list; -// case VCLASS: -// return &o_class(o)->gc_list; -// case VINSTANCE: -// return &o_instance(o)->gc_list; -// case VMETHOD: -// return &(o_method(o))->gc_list; -// default: -// paw_assert(0); -// return NULL; -// } -} - -static void link_gray_(Object *o, Object **pnext, Object **list) -{ - // if (!is_gray(o)) { - // set_gray(o); - // *pnext = *list; - // *list = o; - // } -} - -#define LINK_GRAY(o, L) link_gray_(o, get_gc_list(o), &(L)) - -static void mark_value(paw_Env *P, Value v); - -static void mark_object(paw_Env *P, Object *o) -{ - // if (!o || !is_white(o)) { - // return; - // } - // gc_trace_object("mark", o); - // switch (o->gc_kind) { - // case VUPVALUE: { - // UpValue *u = o_upvalue(o); - // mark_value(P, *u->p.p); - // set_black(u); - // break; - // } -//// case VBIGINT: - // case VSTRING: - // set_black(o); - // break; - // case VMETHOD: - // case VCLOSURE: - // case VCLASS: - // case VINSTANCE: - // case VPROTO: - // case VARRAY: - // case VMAP: - // case VFOREIGN: - // // Put in the gray list to be traversed later. - // LINK_GRAY(o, P->gc_gray); - // break; - - // default: - // paw_assert(PAW_FALSE); // TODO: cases above can be the default - // } -} - -static void mark_value(paw_Env *P, Value v) -{ -// if (pawV_is_object(v)) { -// mark_object(P, v_object(v)); -// } -} - -static void traverse_proto(paw_Env *P, Proto *p) -{ - mark_object(P, cast_object(p->name)); - mark_object(P, cast_object(p->modname)); - for (int i = 0; i < p->nproto; ++i) { - mark_object(P, cast_object(p->p[i])); - } - for (int i = 0; i < p->ndebug; ++i) { - mark_object(P, cast_object(p->v[i].var.name)); - } - for (int i = 0; i < p->nup; ++i) { - mark_object(P, cast_object(p->u[i].var.name)); - } - for (int i = 0; i < p->nk; ++i) { - mark_value(P, p->k[i]); - } -} - -static void traverse_closure(paw_Env *P, Closure *c) -{ - mark_object(P, cast_object(c->p)); - for (int i = 0; i < c->nup; ++i) { - mark_object(P, cast_object(c->up[i])); - } -} - -// TODO: Need the exact class type for this: get it from the instruction -// that modifies the refcount -static void traverse_attrs(paw_Env *P, Value *pv, int n) -{ - for (int i = 0; i < n; ++i) { - mark_value(P, pv[i]); - } -} - -//static void traverse_class(paw_Env *P, Class *c) -//{ -//// traverse_attrs(P, &c->attrs, ) -//} - -static void traverse_instance(paw_Env *P, Instance *i) -{ -// traverse_attrs(P, i->attrs, i->nattrs); -} - -static void traverse_method(paw_Env *P, Method *m) -{ - mark_object(P, v_object(m->self)); - mark_object(P, v_object(m->f)); -} - -//static void traverse_array(paw_Env *P, Array *a) -//{ -// paw_Int itr = PAW_ITER_INIT; -// while (pawA_iter(a, &itr)) { -// mark_value(P, *pawA_get(P, a, itr)); -// } -//} -// -//static void traverse_map(paw_Env *P, Map *m) -//{ -// paw_Int itr = PAW_ITER_INIT; -// while (pawH_iter(m, &itr)) { -// mark_value(P, m->keys[itr]); -// mark_value(P, m->values[itr]); -// } -//} - -static void traverse_foreign(paw_Env *P, Foreign *u) -{ -// traverse_attrs(P, u->attrs, u->nattrs); -} - -static void mark_roots(paw_Env *P) -{ - for (StackPtr p = P->stack.p; p != P->top.p; ++p) { - mark_value(P, *p); - } - for (CallFrame *cf = P->cf; cf; cf = cf->prev) { - mark_object(P, cast_object(cf->fn)); - } - for (UpValue *u = P->up_list; u; u = u->open.next) { - mark_object(P, cast_object(u)); - } - mark_object(P, cast_object(P->libs)); -} - -static void traverse_objects(paw_Env *P) -{ - for (Object **po = &P->gc_gray; *po;) { - Object *o = *po; - - Object **list = get_gc_list(o); - *po = *list; - *list = NULL; - // set_black(o); - - gc_trace_object("traverse", o); - switch (o->gc_kind) { - case VCLOSURE: - traverse_closure(P, o_closure(o)); - break; - case VPROTO: - traverse_proto(P, o_proto(o)); - break; -// case VCLASS: -// traverse_class(P, o_class(o)); -// break; - case VINSTANCE: - traverse_instance(P, o_instance(o)); - break; - case VMETHOD: - traverse_method(P, o_method(o)); - break; -// case VARRAY: -// traverse_array(P, o_array(o)); -// break; -// case VMAP: -// traverse_map(P, o_map(o)); -// break; - case VFOREIGN: - traverse_foreign(P, o_foreign(o)); - break; - default: - paw_assert(0); - } - } -} - -static void mark_phase(paw_Env *P) -{ - mark_roots(P); - traverse_objects(P); -} - -static void sweep_phase(paw_Env *P) -{ - // for (Object **p = &P->gc_all; *p;) { - // Object *o = *p; - // if (is_white(o)) { - // *p = o->gc_next; - // pawG_free_object(P, o); - // } else { - // p = &o->gc_next; - // paw_assert(is_black(o)); - // set_white(o); - // } - // } -} - -static void free_all_objects(paw_Env *P, Object *o) -{ - for (; o; o = o->gc_next) { - pawG_free_object(P, o); - } -} - -static void clean_dead_objects(paw_Env *P) -{ -// for (Object **p = &P->gc_all; *p;) { -// Object *o = *p; -// if (g_hasref(o)) { -// p = &o->gc_next; -// } else { -// *p = o->gc_next; -// pawG_free_object(P, o); -// } -// } -} - -void pawG_collect(paw_Env *P) -{ - clean_dead_objects(P); - - // increase the limit - P->gc_limit = P->gc_bytes * 2; -} - -void pawG_add_object(paw_Env *P, Object *o, ValueKind kind) -{ - gc_trace_object("register", o); - o->gc_kind = kind; - o->gc_next = P->gc_all; - P->gc_all = o; -} - -void pawG_init(paw_Env *P) -{ - P->gc_limit = PAW_GC_LIMIT; -} - -void pawG_uninit(paw_Env *P) -{ - // Free the call frames. - for (CallFrame *cf = P->main.next; cf;) { - CallFrame *next = cf->next; - pawM_free(P, cf); - cf = next; - } - P->cf = NULL; - P->ncf = 0; - -// // Free the managed objects. -// free_all_objects(P, P->gc_fixed); -// free_all_objects(P, P->gc_all); -// P->gc_fixed = NULL; -// P->gc_all = NULL; -} - -void pawG_fix_object(paw_Env *P, Object *o) -{ - // Must be the most-recently-created GC object. -// paw_assert(P->gc_all == o); -// paw_assert(!g_hasref(o)); -// -// g_incref(o); -// P->gc_all = o->gc_next; -// o->gc_next = P->gc_fixed; -// P->gc_fixed = o; -} - -void pawG_free_object(paw_Env *P, Object *o) -{ - switch (o->gc_kind) { - case VUPVALUE: - pawV_free_upvalue(P, o_upvalue(o)); - break; - case VCLOSURE: - pawV_free_closure(P, o_closure(o)); - break; -// case VFOREIGN: -// pawV_free_foreign(P, o_foreign(o)); -// break; - case VSTRING: - pawS_free_str(P, o_string(o)); - break; -// case VARRAY: -// pawA_free(P, o_array(o)); -// break; - case VPROTO: - pawV_free_proto(P, o_proto(o)); - break; -// case VCLASS: -// pawV_free_class(P, o_class(o)); -// break; -// case VINSTANCE: -// pawV_free_instance(P, o_instance(o)); -// break; - case VMETHOD: - pawV_free_method(P, o_method(o)); - break; - default: - paw_assert(PAW_FALSE); - } -} -#endif // 0 diff --git a/src/hir.c b/src/hir.c index 5358ad9..8299ee2 100644 --- a/src/hir.c +++ b/src/hir.c @@ -1,3 +1,4 @@ +// Copyright (c) 2024, The paw Authors. All rights reserved. // This source code is licensed under the MIT License, which can be found in // LICENSE.md. See AUTHORS.md for a list of contributor names. @@ -11,7 +12,7 @@ #include #define LIST_MIN 8 -#define FIRST_ARENA_SIZE 512 +#define FIRST_ARENA_SIZE 4096 #define LARGE_ARENA_MIN 32 static void add_builtin_type(struct Hir *hir, enum HirTypeKind kind, paw_Type code) @@ -42,11 +43,13 @@ struct Hir *pawHir_new(struct Compiler *C) return hir; } -void pawHir_free_hir(struct Hir *hir) +void pawHir_free(struct Hir *hir) { - paw_Env *P = ENV(hir); - pawK_pool_uninit(P, &hir->pool); - pawM_free(P, hir); + if (hir != NULL) { + paw_Env *P = ENV(hir); + pawK_pool_uninit(P, &hir->pool); + pawM_free(P, hir); + } } #define NEW_NODE(hir, T) pawK_pool_alloc(ENV(hir), &(hir)->pool, sizeof(struct T)) @@ -82,8 +85,8 @@ DefId pawHir_add_decl(struct Hir *hir, struct HirDecl *decl) { paw_Env *P = ENV(hir); struct DynamicMem *dm = hir->dm; - pawM_grow(P, dm->decls.data, dm->decls.size, dm->decls.alloc); - const DefId id = dm->decls.size++; + pawM_grow(P, dm->decls.data, dm->decls.count, dm->decls.alloc); + const DefId id = dm->decls.count++; dm->decls.data[id] = decl; decl->hdr.did = id; return id; @@ -92,7 +95,7 @@ DefId pawHir_add_decl(struct Hir *hir, struct HirDecl *decl) struct HirDecl *pawHir_get_decl(struct Hir *hir, DefId did) { struct DynamicMem *dm = hir->dm; - paw_assert(did < dm->decls.size); + paw_assert(did < dm->decls.count); return dm->decls.data[did]; } @@ -501,7 +504,7 @@ static struct HirStmt *FoldBlock(struct HirFolder *F, struct HirBlock *s) s->stmts = F->FoldStmtList(F, s->stmts); return HIR_CAST_STMT(s); } -#define FOLD_BLOCK(F, s) cast((F)->FoldBlock(F, s), struct HirBlock *) +#define FOLD_BLOCK(F, s) CAST((F)->FoldBlock(F, s), struct HirBlock *) static struct HirExpr *FoldLogicalExpr(struct HirFolder *F, struct HirLogicalExpr *e) { @@ -885,7 +888,7 @@ static struct HirStmt *copy_block_stmt(struct HirFolder *F, struct HirBlock *s) return r; } -#define COPY_BLOCK(F, s) cast((F)->FoldBlock(F, s), struct HirBlock *) +#define COPY_BLOCK(F, s) CAST((F)->FoldBlock(F, s), struct HirBlock *) static struct HirExpr *copy_logical_expr(struct HirFolder *F, struct HirLogicalExpr *e) { @@ -1523,7 +1526,7 @@ DEFINE_KIND_PRINTER(decl, HirDecl) DEFINE_KIND_PRINTER(stmt, HirStmt) DEFINE_KIND_PRINTER(type, HirType) -#define dump_block(P, b) check_exp(HirIsBlock(HIR_CAST_STMT(b)), dump_stmt(P, HIR_CAST_STMT(b))) +#define dump_block(P, b) CHECK_EXP(HirIsBlock(HIR_CAST_STMT(b)), dump_stmt(P, HIR_CAST_STMT(b))) #define dump_name(P, s) dump_fmt(P, "name: %s\n", s ? s->text : NULL) static void dump_expr(struct Printer *, struct HirExpr *); diff --git a/src/hir.h b/src/hir.h index 906aa9d..9b48fec 100644 --- a/src/hir.h +++ b/src/hir.h @@ -69,7 +69,7 @@ struct HirSymtab { struct HirScope *globals; }; -#define last_scope(t) check_exp((t)->size > 0, (t)->data[(t)->size - 1]) +#define last_scope(t) CHECK_EXP((t)->size > 0, (t)->data[(t)->size - 1]) struct HirScope *pawHir_new_scope(struct Hir *hir, struct HirSymtab *table); struct HirSymtab *pawHir_new_symtab(struct Hir *hir); void pawHir_add_scope(struct Hir *hir, struct HirSymtab *table, struct HirScope *scope); @@ -688,7 +688,7 @@ struct HirDecl *pawHir_get_decl(struct Hir *hir, DefId id); // NOTE: HirFuncPtr and HirFuncDef share the same common initial sequence, so // HirFuncPtr fields can be accessed on a HirFuncDef -#define HIR_FPTR(t) check_exp(HirIsFuncType(t), &(t)->fptr) +#define HIR_FPTR(t) CHECK_EXP(HirIsFuncType(t), &(t)->fptr) static inline struct HirType *hir_vector_elem(struct HirType *t) { diff --git a/src/lex.c b/src/lex.c index 13ef1f0..ed76c43 100644 --- a/src/lex.c +++ b/src/lex.c @@ -4,7 +4,7 @@ #include "lex.h" #include "auxlib.h" #include "compile.h" -#include "gc_aux.h" +#include "gc.h" #include "map.h" #include "mem.h" #include "parse.h" @@ -17,7 +17,7 @@ #define lex_error(x) pawX_error(x, "syntax error") #define save_and_next(x) (save(x, (x)->c), next(x)) -#define is_eof(x) (cast((x)->c, uint8_t) == TK_END) +#define is_eof(x) (CAST((x)->c, uint8_t) == TK_END) #define is_newline(x) ((x)->c == '\r' || (x)->c == '\n') static void add_location(paw_Env *P, Buffer *print, const String *s, int line) @@ -54,7 +54,7 @@ static char next_raw(struct Lex *x) --x->nchunk; ++x->chunk; } else { - x->c = cast(TK_END, char); + x->c = CAST(TK_END, char); } return x->c; } @@ -80,8 +80,8 @@ static char next(struct Lex *x) static void save(struct Lex *x, char c) { struct DynamicMem *dm = x->dm; - pawM_grow(ENV(x), dm->scratch.data, dm->scratch.size, dm->scratch.alloc); - dm->scratch.data[dm->scratch.size++] = c; + pawM_grow(ENV(x), dm->scratch.data, dm->scratch.count, dm->scratch.alloc); + dm->scratch.data[dm->scratch.count++] = c; } static paw_Bool test_next(struct Lex *x, char c) @@ -136,9 +136,9 @@ static struct Token make_string(struct Lex *x, TokenKind kind) struct DynamicMem *dm = x->dm; struct CharVec *cv = &dm->scratch; struct Token t = make_token(kind); - String *s = pawP_scan_nstring(ENV(x), x->strings, cv->data, cast_size(cv->size)); + String *s = pawP_scan_nstring(ENV(x), x->strings, cv->data, CAST_SIZE(cv->count)); v_set_object(&t.value, s); - cv->size = 0; + cv->count = 0; return t; } @@ -151,7 +151,7 @@ static struct Token consume_name(struct Lex *x) struct Token t = make_string(x, TK_NAME); const String *s = v_string(t.value); if (IS_KEYWORD(s)) { - t.kind = cast(s->flag, TokenKind); + t.kind = CAST(s->flag, TokenKind); } else if (s->length > PAW_NAME_MAX) { pawX_error(x, "name (%I chars) is too long", paw_cast_int(s->length)); } @@ -195,7 +195,7 @@ static int consume_utf8(struct Lex *x) uint32_t state = 0; do { - const uint8_t c = cast(x->c, uint8_t); + const uint8_t c = CAST(x->c, uint8_t); state = kLookup1[c] + state * 12; state = kLookup2[state]; save_and_next(x); @@ -420,13 +420,13 @@ static void skip_whitespace(struct Lex *x) static struct Token advance(struct Lex *x) { try_again: -#define T(kind) make_token(cast(kind, TokenKind)) +#define T(kind) make_token(CAST(kind, TokenKind)) skip_whitespace(x); // cast to avoid sign extension - struct Token token = T(cast(x->c, uint8_t)); + struct Token token = T(CAST(x->c, uint8_t)); paw_Bool semi = PAW_FALSE; - x->dm->scratch.size = 0; + x->dm->scratch.count = 0; switch (x->c) { case '\n': case '\r': @@ -560,8 +560,11 @@ void pawX_set_source(struct Lex *x, paw_Reader input, void *ud) { paw_Env *P = ENV(x); struct DynamicMem *dm = x->dm; - pawM_resize(P, dm->scratch.data, 0, INITIAL_SCRATCH); - dm->scratch.alloc = INITIAL_SCRATCH; + if (dm->scratch.alloc < INITIAL_SCRATCH) { + pawM_resize(P, dm->scratch.data, dm->scratch.alloc, INITIAL_SCRATCH); + dm->scratch.alloc = INITIAL_SCRATCH; + } + dm->scratch.count = 0; x->ud = ud; x->input = input; diff --git a/src/lib.c b/src/lib.c index faf549c..79ec217 100644 --- a/src/lib.c +++ b/src/lib.c @@ -5,7 +5,7 @@ #include "api.h" #include "auxlib.h" #include "call.h" -#include "gc_aux.h" +#include "gc.h" #include "map.h" #include "mem.h" #include "os.h" @@ -15,7 +15,7 @@ #include #include -#define cf_base(i) P->cf->base.p[i] +#define CF_BASE(i) P->cf->base.p[i] void lib_error(paw_Env *P, int error, const char *fmt, ...) { @@ -47,8 +47,8 @@ int pawL_check_varargc(paw_Env *P, int min, int max) // static void try_aux(paw_Env *P, void *arg) //{ -// const int argc = *cast(arg, int *); -// const Value f = cf_base(1); +// const int argc = *CAST(arg, int *); +// const Value f = CF_BASE(1); // pawC_call(P, v_object(f), argc - 1); // } // @@ -130,7 +130,7 @@ int pawL_check_varargc(paw_Env *P, int min, int max) static int base_assert(paw_Env *P) { - if (v_false(cf_base(1))) { + if (v_false(CF_BASE(1))) { pawR_error(P, PAW_ERUNTIME, "assertion failed"); } return 0; @@ -201,22 +201,22 @@ static int base_print(paw_Env *P) static int vector_insert(paw_Env *P) { - Vector *vec = v_vector(cf_base(1)); - const paw_Int index = v_int(cf_base(2)); - pawV_vec_insert(P, vec, index, cf_base(3)); + Vector *vec = v_vector(CF_BASE(1)); + const paw_Int index = v_int(CF_BASE(2)); + pawV_vec_insert(P, vec, index, CF_BASE(3)); return 0; } static int vector_push(paw_Env *P) { - Vector *vec = v_vector(cf_base(1)); - pawV_vec_push(P, vec, cf_base(2)); + Vector *vec = v_vector(CF_BASE(1)); + pawV_vec_push(P, vec, CF_BASE(2)); return 0; } static int vector_pop(paw_Env *P) { - Vector *vec = v_vector(cf_base(1)); + Vector *vec = v_vector(CF_BASE(1)); const paw_Int length = paw_cast_int(pawV_vec_length(vec)); if (length == 0) { pawR_error(P, PAW_EVALUE, "pop from empty Vector"); @@ -228,7 +228,7 @@ static int vector_pop(paw_Env *P) static paw_Int clamped_index(paw_Env *P, int loc, paw_Int n) { - const paw_Int i = v_int(cf_base(loc)); + const paw_Int i = v_int(CF_BASE(loc)); return i < 0 ? 0 : i >= n ? n - 1 : i; } @@ -240,12 +240,12 @@ static paw_Int clamped_index(paw_Env *P, int loc, paw_Int n) // equality between user-defined types right now. static int vector_remove(paw_Env *P) { - Vector *vec = v_vector(cf_base(1)); + Vector *vec = v_vector(CF_BASE(1)); const paw_Int length = paw_cast_int(pawV_vec_length(vec)); if (length == 0) { pawR_error(P, PAW_EVALUE, "remove from empty Vector"); } - const paw_Int index = v_int(cf_base(2)); + const paw_Int index = v_int(CF_BASE(2)); P->top.p[-1] = *pawV_vec_get(P, vec, index); pawV_vec_pop(P, vec, index); return 1; @@ -253,7 +253,7 @@ static int vector_remove(paw_Env *P) static int vector_clone(paw_Env *P) { - const Vector *vec = v_vector(cf_base(1)); + const Vector *vec = v_vector(CF_BASE(1)); Value *pv = pawC_push0(P); pawV_vec_clone(P, pv, vec); return 1; @@ -261,7 +261,7 @@ static int vector_clone(paw_Env *P) // static String *check_string(paw_Env *P, int i) //{ -// const Value v = cf_base(i); +// const Value v = CF_BASE(i); // if (v_type(v) != VSTRING) { // pawR_error(P, PAW_ETYPE, "expected string"); // } @@ -277,7 +277,7 @@ static const char *find_substr(const char *str, size_t nstr, const char *sub, const char *ptr = str; const char *end = str + nstr; while ((ptr = strchr(ptr, sub[0]))) { - if (nsub <= cast_size(end - ptr) && 0 == memcmp(ptr, sub, nsub)) { + if (nsub <= CAST_SIZE(end - ptr) && 0 == memcmp(ptr, sub, nsub)) { return ptr; } str = ptr + nsub; @@ -288,8 +288,8 @@ static const char *find_substr(const char *str, size_t nstr, const char *sub, static int string_find(paw_Env *P) { pawL_check_argc(P, 1); - const String *s = v_string(cf_base(0)); - const String *find = v_string(cf_base(1)); + const String *s = v_string(CF_BASE(0)); + const String *find = v_string(CF_BASE(1)); const char *result = find_substr(s->text, s->length, find->text, find->length); if (result) { // index of substring @@ -305,8 +305,8 @@ static int string_find(paw_Env *P) // static int string_split(paw_Env *P) //{ // pawL_check_argc(P, 1); -// const String *sep = v_string(cf_base(1)); -// String *s = v_string(cf_base(0)); +// const String *sep = v_string(CF_BASE(1)); +// String *s = v_string(CF_BASE(0)); // if (sep->length == 0) { // pawR_error(P, PAW_EVALUE, "empty separator"); // } @@ -316,7 +316,7 @@ static int string_find(paw_Env *P) // size_t nstr = s->length; // const char *pstr = s->text; // while ((part = find_substr(pstr, nstr, sep->text, sep->length))) { -// const size_t n = cast_size(part - pstr); +// const size_t n = CAST_SIZE(part - pstr); // pawC_pushns(P, pstr, n); // part += sep->length; // skip separator // pstr = part; @@ -324,7 +324,7 @@ static int string_find(paw_Env *P) // ++npart; // } // const char *end = s->text + s->length; // add the rest -// pawC_pushns(P, pstr, cast_size(end - pstr)); +// pawC_pushns(P, pstr, CAST_SIZE(end - pstr)); // ++npart; // // pawR_literal_array(P, npart); @@ -334,8 +334,8 @@ static int string_find(paw_Env *P) // static int string_join(paw_Env *P) //{ // pawL_check_argc(P, 1); -// const Value seq = cf_base(1); -// String *s = v_string(cf_base(0)); +// const Value seq = CF_BASE(1); +// String *s = v_string(CF_BASE(0)); // // Buffer buf; // pawL_init_buffer(P, &buf); @@ -346,7 +346,7 @@ static int string_find(paw_Env *P) // // Add a chunk, followed by the separator if necessary. // const String *chunk = v_string(v); // pawL_add_nstring(P, &buf, chunk->text, chunk->length); -// if (cast_size(itr + 1) < pawA_length(a)) { +// if (CAST_SIZE(itr + 1) < pawA_length(a)) { // pawL_add_nstring(P, &buf, s->text, s->length); // } // } @@ -357,8 +357,8 @@ static int string_find(paw_Env *P) static int string_starts_with(paw_Env *P) { pawL_check_argc(P, 1); - String *s = v_string(cf_base(0)); - const String *prefix = v_string(cf_base(1)); + String *s = v_string(CF_BASE(0)); + const String *prefix = v_string(CF_BASE(1)); const size_t prelen = prefix->length; const paw_Bool b = s->length >= prelen && 0 == memcmp(prefix->text, s->text, prelen); @@ -369,8 +369,8 @@ static int string_starts_with(paw_Env *P) static int string_ends_with(paw_Env *P) { pawL_check_argc(P, 1); - String *s = v_string(cf_base(0)); - const String *suffix = v_string(cf_base(1)); + String *s = v_string(CF_BASE(0)); + const String *suffix = v_string(CF_BASE(1)); const size_t suflen = suffix->length; paw_Bool b = PAW_FALSE; if (s->length >= suflen) { @@ -392,8 +392,8 @@ static int string_clone(paw_Env *P) static int map_get(paw_Env *P) { - const Value key = cf_base(1 /*TODO*/ + 1); - Map *m = v_map(cf_base(0 /*TODO*/ + 1)); + const Value key = CF_BASE(1 /*TODO*/ + 1); + Map *m = v_map(CF_BASE(0 /*TODO*/ + 1)); const Value *pv = pawH_get(P, m, key); if (pv != NULL) { // replace default value @@ -404,14 +404,14 @@ static int map_get(paw_Env *P) static int map_erase(paw_Env *P) { - Map *m = v_map(cf_base(0 /*TODO*/ + 1)); - pawH_remove(P, m, cf_base(1 /*TODO*/ + 1)); + Map *m = v_map(CF_BASE(0 /*TODO*/ + 1)); + pawH_remove(P, m, CF_BASE(1 /*TODO*/ + 1)); return 0; } static int map_clone(paw_Env *P) { - Map *m = v_map(cf_base(0 /*TODO*/ + 1)); + Map *m = v_map(CF_BASE(0 /*TODO*/ + 1)); Value *pv = pawC_push0(P); pawH_clone(P, pv, m); return 1; @@ -441,8 +441,8 @@ static void add_builtin_func(paw_Env *P, const char *name, paw_Function func) void pawL_init(paw_Env *P) { Value *pv = pawC_push0(P); - P->builtin = pawH_new(P); P->libs = pawH_new(P); + P->builtin = pawH_new(P); v_set_object(pv, P->builtin); // Builtin functions: diff --git a/src/map.c b/src/map.c index 098e450..4739258 100644 --- a/src/map.c +++ b/src/map.c @@ -2,7 +2,7 @@ // This source code is licensed under the MIT License, which can be found in // LICENSE.md. See AUTHORS.md for a list of contributor names. #include "map.h" -#include "gc_aux.h" +#include "gc.h" #include "mem.h" #include "rt.h" #include "util.h" @@ -94,13 +94,13 @@ static void grow_map(paw_Env *P, Map *m) rehash_map(old_m, m); } free_buffer(P, old_m.data, old_m.capacity); - check_gc(P); + CHECK_GC(P); } Map *pawH_new(paw_Env *P) { Map *m = pawM_new(P, Map); - pawG_add_object(P, cast_object(m), VMAP); + pawG_add_object(P, CAST_OBJECT(m), VMAP); return m; } @@ -122,9 +122,9 @@ Value *pawH_create(paw_Env *P, Map *m, Value key) void pawH_clone(paw_Env *P, StackPtr sp, Map *m) { - Map *m2 = pawH_new(P); - v_set_object(sp, m2); - pawH_extend(P, m2, m); + Map *clone = pawH_new(P); + v_set_object(sp, clone); + pawH_extend(P, clone, m); } static paw_Bool items_equal(Value x, Value y) @@ -142,7 +142,7 @@ paw_Bool pawH_equals(paw_Env *P, Map *lhs, Map *rhs) MapCursor mc = {lhs, 0}; while (mc.index < lhs->capacity) { Value *v = pawH_action(P, rhs, *h_cursor_key(&mc), MAP_ACTION_NONE); - if (!v || !items_equal(*h_cursor_value(&mc), *v)) { + if (v == NULL || !items_equal(*h_cursor_value(&mc), *v)) { return PAW_FALSE; } ++mc.index; diff --git a/src/map.h b/src/map.h index c07a5fd..5cee0d4 100644 --- a/src/map.h +++ b/src/map.h @@ -17,13 +17,13 @@ typedef struct MapCursor { #define h_is_occupied(mc) (h_get_state(mc) == MAP_ITEM_OCCUPIED) #define h_is_erased(mc) (h_get_state(mc) == MAP_ITEM_ERASED) -#define pawH_meta(m, index) (&cast((m)->data, MapMeta *)[index]) +#define pawH_meta(m, index) (&CAST((m)->data, MapMeta *)[index]) static inline Value *pawH_key(Map *m, size_t index) { - char *data = cast(m->data, char *); + char *data = CAST(m->data, char *); data += sizeof(MapMeta) * m->capacity; - return &cast(data, Value *)[index]; + return &CAST(data, Value *)[index]; } static inline Value *pawH_value(Map *m, size_t index) @@ -33,12 +33,12 @@ static inline Value *pawH_value(Map *m, size_t index) static inline MapState h_get_state(MapCursor *mc) { - return cast(mc->map->data, MapMeta *)[mc->index].state; + return CAST(mc->map->data, MapMeta *)[mc->index].state; } static inline void h_set_state(MapCursor *mc, MapState state) { - cast(mc->map->data, MapMeta *)[mc->index].state = state; + CAST(mc->map->data, MapMeta *)[mc->index].state = state; } static inline Value *h_cursor_key(MapCursor *mc) diff --git a/src/mem.c b/src/mem.c index 6620b96..ee1201d 100644 --- a/src/mem.c +++ b/src/mem.c @@ -2,7 +2,8 @@ // This source code is licensed under the MIT License, which can be found in // LICENSE.md. See AUTHORS.md for a list of contributor names. #include "mem.h" -#include "gc_aux.h" +#include "alloc.h" +#include "gc.h" #include #include #include @@ -10,8 +11,8 @@ static void *first_try(paw_Env *P, void *ptr, size_t size0, size_t size) { -#if PAW_STRESS > 0 - if (size && !P->gc_noem) { +//#if PAW_STRESS > 0 + if (size > 0 && !P->gc_noem) { // Fail on the first attempt to get more memory, but only if the // runtime is allowed to perform emergency collections. Otherwise, // PAW_STRESS > 0 would not be compatible with certain operations, @@ -19,32 +20,30 @@ static void *first_try(paw_Env *P, void *ptr, size_t size0, size_t size) // not allowed. return NULL; } -#endif - return P->alloc(P->ud, ptr, size0, size); +//#endif + return pawZ_alloc(P, ptr, size0, size); } static void *try_again(paw_Env *P, void *ptr, size_t size0, size_t size) { pawG_collect(P); // emergency collection - return P->alloc(P->ud, ptr, size0, size); + return pawZ_alloc(P, ptr, size0, size); } static void *m_alloc(paw_Env *P, void *ptr, size_t size0, size_t size) { if (size == 0) { - if (ptr == NULL) { - return NULL; - } + if (ptr == NULL) return NULL; P->gc_bytes -= size0; // 'free' never fails - return P->alloc(P->ud, ptr, size0, 0); + return pawZ_alloc(P, ptr, size0, 0); } // (re)allocate memory void *ptr2 = first_try(P, ptr, size0, size); - if (!ptr2 && !P->gc_noem) { - // Run an emergency collection and try again. + if (ptr2 == NULL && !P->gc_noem) { + // run an emergency collection and try again ptr2 = try_again(P, ptr, size0, size); } - if (ptr2) { + if (ptr2 != NULL) { P->gc_bytes += size - size0; } return ptr2; @@ -62,9 +61,7 @@ void pawM_free_(paw_Env *P, void *ptr, size_t size) void *pawM_new_vec_(paw_Env *P, size_t n, size_t elem_sz) { - if (n == 0) { - return NULL; - } + if (n == 0) return NULL; pawM_check_size(P, 0, n, elem_sz); void *ptr = pawM_alloc(P, NULL, 0, n * elem_sz); if (!ptr) { @@ -78,9 +75,8 @@ void *pawM_new_flex_(paw_Env *P, size_t obj_sz, size_t n, size_t elem_sz) { pawM_check_size(P, obj_sz, n, elem_sz); void *ptr = pawM_alloc(P, NULL, 0, obj_sz + n * elem_sz); - if (!ptr) { - pawM_error(P); - } + if (ptr == NULL) pawM_error(P); + // clear non-flex part memset(ptr, 0, obj_sz); return ptr; @@ -101,25 +97,21 @@ void *pawM_grow_(paw_Env *P, void *ptr, int n, int *p_alloc, size_t elem_sz) } else { cap *= 2; } - ptr = pawM_resize_(P, ptr, cast_size(*p_alloc), cast_size(cap), elem_sz); + ptr = pawM_resize_(P, ptr, CAST_SIZE(*p_alloc), CAST_SIZE(cap), elem_sz); *p_alloc = cap; return ptr; } -void *pawM_shrink_(paw_Env *P, void *ptr, int *palloc0, int alloc, - size_t elem_sz) +void *pawM_shrink_(paw_Env *P, void *ptr, int *palloc0, int alloc, size_t elem_sz) { paw_assert(*palloc0 >= alloc); - if (*palloc0 == alloc) { - return ptr; - } - ptr = pawM_resize_(P, ptr, cast_size(*palloc0), cast_size(alloc), elem_sz); + if (*palloc0 == alloc) return ptr; + ptr = pawM_resize_(P, ptr, CAST_SIZE(*palloc0), CAST_SIZE(alloc), elem_sz); *palloc0 = alloc; return ptr; } -void *pawM_resize_(paw_Env *P, void *ptr, size_t alloc0, size_t alloc, - size_t elem_sz) +void *pawM_resize_(paw_Env *P, void *ptr, size_t alloc0, size_t alloc, size_t elem_sz) { void *ptr2 = pawM_alloc(P, ptr, alloc0 * elem_sz, alloc * elem_sz); if (alloc && !ptr2) { diff --git a/src/mem.h b/src/mem.h index 902d5d1..9c8b018 100644 --- a/src/mem.h +++ b/src/mem.h @@ -17,21 +17,21 @@ #define pawM_free(P, ptr) pawM_free_vec(P, ptr, 1) #define pawM_new_vec(P, n, type) \ - (type *)pawM_new_vec_(P, cast_size(n), sizeof(type)) + (type *)pawM_new_vec_(P, CAST_SIZE(n), sizeof(type)) #define pawM_free_vec(P, ptr, n) \ - pawM_free_(P, ptr, cast_size(n) * sizeof((ptr)[0])) + pawM_free_(P, ptr, CAST_SIZE(n) * sizeof((ptr)[0])) #define pawM_new_flex(P, tobj, n, e) \ - (tobj *)pawM_new_flex_(P, sizeof(tobj), cast_size(n), e) + (tobj *)pawM_new_flex_(P, sizeof(tobj), CAST_SIZE(n), e) #define pawM_free_flex(P, ptr, n, e) \ - pawM_free_(P, ptr, sizeof(*(ptr)) + (cast_size(n) * cast_size(e))) + pawM_free_(P, ptr, sizeof(*(ptr)) + (CAST_SIZE(n) * CAST_SIZE(e))) #define pawM_grow(P, ptr, size, alloc) \ ((ptr) = pawM_grow_(P, ptr, size, &(alloc), sizeof((ptr)[0]))) #define pawM_shrink(P, ptr, alloc0, alloc) \ ((ptr) = pawM_shrink_(P, ptr, &(alloc0), alloc, sizeof((ptr)[0]))) #define pawM_resize(P, ptr, alloc0, alloc) \ - ((ptr) = pawM_resize_aux(P, ptr, cast_size(alloc0), cast_size(alloc))) + ((ptr) = pawM_resize_aux(P, ptr, CAST_SIZE(alloc0), CAST_SIZE(alloc))) // Ensure that the expression 'o + n * e' will not wrap #define pawM_check_size(P, o, n, e) \ diff --git a/src/meta.c b/src/meta.c deleted file mode 100644 index 31f9543..0000000 --- a/src/meta.c +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2024, The paw Authors. All rights reserved. -// This source code is licensed under the MIT License, which can be found in -// LICENSE.md. See AUTHORS.md for a list of contributor names. - -#include "meta.h" -#include "call.h" -#include "env.h" -#include "map.h" - -// ORDER Metamethod -const char *pawT_name(Metamethod mm) -{ - static const char *kMetaNames[] = { - "__call", - - "__getattr", - "__setattr", - "__getitem", - "__setitem", - "__getslice", - "__setslice", - - "__bool", - "__int", - "__float", - "__string", - "__array", - "__map", - - "__len", - "__neg", - "__not", - "__bnot", - - "__eq", - "__ne", - "__lt", - "__le", - "__gt", - "__ge", - "__contains", - "__cast", - - "__add", - "__sub", - "__mul", - "__div", - "__mod", - "__bxor", - "__band", - "__bor", - "__shl", - "__shr", - - "__radd", - "__rsub", - "__rmul", - "__rdiv", - "__rmod", - "__rbxor", - "__rband", - "__rbor", - "__rshl", - "__rshr", - - "__init", - "__null", - }; - _Static_assert(paw_countof(kMetaNames) == NMETAMETHODS, - "metamethod names re inconsistent"); - paw_assert(mm < NMETAMETHODS); - return kMetaNames[mm]; -} diff --git a/src/meta.h b/src/meta.h deleted file mode 100644 index 2101b61..0000000 --- a/src/meta.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2024, The paw Authors. All rights reserved. -// This source code is licensed under the MIT License, which can be found in -// LICENSE.md. See AUTHORS.md for a list of contributor names. -#ifndef PAW_META_H -#define PAW_META_H - -#include "opcode.h" -#include "paw.h" -#include "value.h" - -// paw Metamethod descriptions: -// -// Unary operators: -// -// Expression | Metamethod call -// ------------|----------------- -// x | x.__() -// -// Where = -// - = __neg -// ~ = __bnot -// ! = __not -// # = __len -// -// Binary arithmetic operators: -// -// Expression | Metamethod call -// ------------|----------------- -// x y | x.__(y) -// y x | x.__r(y) -// -// Where = -// + = __add -// - = __sub -// * = __mul -// / = __div -// // = __idiv -// % = __mod -// & = __band -// | = __bor -// ^ = __bxor -// -// Binary relational operators: -// -// Expression | Metamethod call -// -------------|----------------- -// x < y | x.__lt(y) -// x <= y | x.__le(y) -// x > y | x.__gt(y) -// x >= y | x.__ge(y) -// y < x | x.__ge(y) -// y <= x | x.__gt(y) -// y > x | x.__le(y) -// y >= x | x.__lt(y) -// -// Binary equality operators: -// -// Expression | Metamethod call -// ------------|----------------- -// x == y | x.__eq(y) -// y == x | x.__eq(y) -// x != y | !x.__eq(y) -// y != x | !x.__eq(y) - -#define mm_has_r(op) ((op) >= MM_ADD) -#define mm_get_r(op) ((op) + (MM_RADD - MM_ADD)) - -const char *pawT_name(unsigned op); - -#endif // PAW_META_H diff --git a/src/parse.c b/src/parse.c index ec1c3a9..3065220 100644 --- a/src/parse.c +++ b/src/parse.c @@ -275,9 +275,8 @@ static struct AstDecl *vfield_decl(struct Lex *lex) static void parse_##name##_list(struct Lex *lex, struct L *list, int line) \ { \ do { \ - if (test(lex, b)) { \ - break; \ - } else if ((list)->count == (limit)) { \ + if (test(lex, b)) break; \ + if ((list)->count == (limit)) { \ limit_error(lex, what, (limit)); \ } \ prefix##push((lex)->dm->ast, list, (func)(lex)); \ @@ -349,9 +348,8 @@ static void parse_tuple_type(struct Lex *lex, struct AstExpr *pe, int line) pawAst_expr_list_push(lex->ast, elems, first); do { - if (test(lex, ')')) { - break; - } else if (elems->count == FIELD_MAX) { + if (test(lex, ')')) break; + if (elems->count == FIELD_MAX) { limit_error(lex, "tuple elements", FIELD_MAX); } struct AstExpr *type = type_expr(lex); @@ -563,7 +561,7 @@ static struct AstExpr *unop_expr(struct Lex *lex, enum UnOp op) struct AstExpr *result = pawAst_new_expr(lex->ast, kAstUnOpExpr); struct AstUnOpExpr *r = &result->unop; skip(lex); // unary operator token - r->op = cast(op, UnaryOp); // same order + r->op = CAST(op, UnaryOp); // same order r->target = expression(lex, kUnOpPrecedence); return result; } @@ -639,9 +637,8 @@ static paw_Type parse_container_items(struct Lex *lex, struct AstExprList **pite struct AstExprList *items = *pitems; paw_Type code = -1; do { - if (test(lex, ']')) { - break; - } else if (items->count == LOCAL_MAX) { + if (test(lex, ']')) break; + if (items->count == LOCAL_MAX) { limit_error(lex, "container literal items", LOCAL_MAX); } struct AstExpr *item = expression0(lex); @@ -904,7 +901,7 @@ static struct AstExpr *binop_expr(struct Lex *lex, enum InfixOp op, struct AstEx return NULL; // no more binops } struct AstExpr *r = pawAst_new_expr(lex->ast, kAstBinOpExpr); - r->binop.op = cast(op, BinaryOp); // same order + r->binop.op = CAST(op, BinaryOp); // same order r->binop.lhs = lhs; r->binop.rhs = rhs; return r; @@ -1142,9 +1139,8 @@ static struct AstDecl *variant_decl(struct Lex *lex, int index) static void parse_variant_list(struct Lex *lex, struct AstDeclList *list, int line) { do { - if (test(lex, '}')) { - break; - } else if (list->count == LOCAL_MAX) { + if (test(lex, '}')) break; + if (list->count == LOCAL_MAX) { limit_error(lex, "variants", LOCAL_MAX); } // NOTE: 'variant_decl' requires a second argument, so 'DEFINE_LIST_PARSER' @@ -1410,11 +1406,6 @@ void pawP_init(paw_Env *P) String *str = pawS_new_fixed(P, kw); str->flag = i + FIRST_KEYWORD; } - for (Metamethod mm = 0; mm < NMETAMETHODS; ++mm) { - const char *name = pawT_name(mm); - String *str = pawS_new_fixed(P, name); - v_set_object(&P->meta_keys[mm], str); - } P->str_cache[CSTR_SELF] = pawS_new_str(P, "self"); P->str_cache[CSTR_TRUE] = pawS_new_str(P, "true"); P->str_cache[CSTR_FALSE] = pawS_new_str(P, "false"); diff --git a/src/parse.h b/src/parse.h index b9e5c73..8bb1a9f 100644 --- a/src/parse.h +++ b/src/parse.h @@ -49,12 +49,6 @@ struct LocalSlot { paw_Bool is_captured : 1; }; -struct LocalStack { - struct LocalSlot *slots; - int nslots; - int capacity; -}; - enum FuncKind { FUNC_MODULE, FUNC_CLOSURE, @@ -67,11 +61,11 @@ struct FuncState { struct FuncType *type; // function signature struct Generator *G; // codegen state struct HirSymtab *scopes; // local scopes - struct LocalStack locals; // local variables struct BlockState *bs; // current block Proto *proto; // prototype being built String *name; // name of the function - int level; // current stack index (base = 0) + int first_local; // index of function in DynamicMem array + int nlocals; // number of locals int nup; // number of upvalues int nk; // number of constants int nproto; // number of nested functions @@ -85,16 +79,22 @@ struct DynamicMem { // Buffer for accumulating strings struct CharVec { char *data; - int size; + int count; int alloc; } scratch; struct { struct HirDecl **data; - int size; + int count; int alloc; } decls; + struct { + struct LocalSlot *data; + int count; + int alloc; + } vars; + struct Ast *ast; struct Hir *hir; diff --git a/src/check.c b/src/resolve.c similarity index 99% rename from src/check.c rename to src/resolve.c index 13777d0..e2d15a5 100644 --- a/src/check.c +++ b/src/resolve.c @@ -10,7 +10,7 @@ #include "compile.h" #include "debug.h" #include "env.h" -#include "gc_aux.h" +#include "gc.h" #include "hir.h" #include "map.h" #include "mem.h" @@ -23,7 +23,7 @@ #define NAME_ERROR(R, line, ...) pawE_error(ENV(R), PAW_ENAME, line, __VA_ARGS__) #define SYNTAX_ERROR(R, ...) pawE_error(ENV(R), PAW_ESYNTAX, -1, __VA_ARGS__) #define TYPE_ERROR(R, ...) pawE_error(ENV(R), PAW_ETYPE, -1, __VA_ARGS__) -#define CACHED_STR(R, i) pawE_cstr(ENV(R), cast_size(i)) +#define CACHED_STR(R, i) pawE_cstr(ENV(R), CAST_SIZE(i)) #define TYPE2CODE(R, type) (pawP_type2code((R)->C, type)) struct ItemSlot { @@ -80,7 +80,7 @@ static void unify(struct Resolver *R, struct HirType *a, struct HirType *b) static struct HirType *get_type(struct Resolver *R, DefId did) { - paw_assert(did < R->dm->decls.size); + paw_assert(did < R->dm->decls.count); return HIR_TYPEOF(R->dm->decls.data[did]); } @@ -97,7 +97,7 @@ static paw_Bool is_map_t(struct Resolver *R, struct HirType *type) static struct HirDecl *get_decl(struct Resolver *R, DefId did) { struct DynamicMem *dm = R->dm; - paw_assert(did < dm->decls.size); + paw_assert(did < dm->decls.count); return dm->decls.data[did]; } @@ -221,7 +221,7 @@ static void pop_symbol_table(struct Resolver *R) static void sanity_check(struct Resolver *R, struct HirDecl *new_decl) { struct DynamicMem *dm = R->dm; - for (DefId did = 0; did < dm->decls.size; ++did) { + for (DefId did = 0; did < dm->decls.count; ++did) { struct HirDecl *old_decl = dm->decls.data[did]; paw_assert(old_decl->hdr.did == did); paw_assert(old_decl != new_decl); @@ -403,7 +403,7 @@ static void enter_block(struct Resolver *R, struct HirScope *scope) static struct HirScope *leave_function(struct Resolver *R) { struct HirScope *scope = leave_block(R); - check_gc(ENV(R)); + CHECK_GC(ENV(R)); --R->func_depth; return scope; } @@ -1633,7 +1633,7 @@ static struct HirType *resolve_composite_lit(struct Resolver *R, struct AstCompo } paw_Int iter = PAW_ITER_INIT; while (pawH_iter(map, &iter)) { - const Value *pkey = pawH_key(map, cast_size(iter)); + const Value *pkey = pawH_key(map, CAST_SIZE(iter)); NAME_ERROR(R, -1, "unexpected field '%s' in initializer for struct '%s'", v_string(*pkey), pack.name->text); } @@ -1654,7 +1654,7 @@ static struct HirExpr *resolve_literal_expr(struct Resolver *R, struct AstLitera struct HirLiteralExpr *r = HirGetLiteralExpr(result); // literal kinds correspond 1-to-1 between AST and HIR - r->lit_kind = cast(e->lit_kind, enum HirLitKind); + r->lit_kind = CAST(e->lit_kind, enum HirLitKind); if (e->lit_kind == kAstBasicLit) { r->type = resolve_basic_lit(R, &e->basic, &r->basic); diff --git a/src/rt.c b/src/rt.c index 8614381..6cfbf88 100644 --- a/src/rt.c +++ b/src/rt.c @@ -3,25 +3,12 @@ // LICENSE.md. See AUTHORS.md for a list of contributor names. #include "prefix.h" -#include "auxlib.h" -#include "call.h" #include "env.h" -#include "gc_aux.h" -#include "lex.h" -#include "lib.h" +#include "call.h" +#include "gc.h" #include "map.h" -#include "mem.h" -#include "opcode.h" -#include "os.h" -#include "paw.h" -#include "rt.h" -#include "str.h" -#include "type.h" -#include "util.h" #include "value.h" -#include #include -#include // Helpers for the VM: #define vm_switch(x) switch (x) @@ -43,7 +30,7 @@ #define vm_pushi(i) pawC_pushi(P, i) #define vm_pushf(f) pawC_pushf(P, f) #define vm_pushb(b) pawC_pushb(P, b) -#define vm_pusho(o) pawC_pusho(P, cast_object(o)) +#define vm_pusho(o) pawC_pusho(P, CAST_OBJECT(o)) // Slot 0 (the callable) is an implicit parameter. #define vm_argc() (paw_get_count(P) - 1) @@ -190,7 +177,7 @@ void pawR_cast_float(paw_Env *P, paw_Type type) if (type != PAW_TFLOAT) { Value *pv = vm_top(1); const paw_Int i = v_int(*pv); - v_set_float(pv, cast(i, paw_Float)); + v_set_float(pv, CAST(i, paw_Float)); } } @@ -362,7 +349,7 @@ static size_t check_index(paw_Env *P, paw_Int index, size_t length, "index %I is out of bounds for %s of length %I", index, what, paw_cast_int(length)); } - return cast_size(index); + return CAST_SIZE(index); } static void setslice_vector(paw_Env *P, Vector *va, paw_Int i, paw_Int j, @@ -406,7 +393,7 @@ void pawR_setslice(paw_Env *P, paw_Type t) void pawR_init(paw_Env *P) { String *errmsg = pawS_new_str(P, "not enough memory"); - pawG_fix_object(P, cast_object(errmsg)); + pawG_fix_object(P, CAST_OBJECT(errmsg)); v_set_object(&P->mem_errmsg, errmsg); } @@ -478,7 +465,7 @@ static paw_Bool formap_init(paw_Env *P) paw_Int itr = PAW_ITER_INIT; Map *map = v_map(v); if (pawH_iter(map, &itr)) { - const Value v = *pawH_key(map, cast_size(itr)); + const Value v = *pawH_key(map, CAST_SIZE(itr)); vm_pushi(itr); vm_pushv(v); return PAW_FALSE; @@ -493,7 +480,7 @@ static paw_Bool formap(paw_Env *P) Map *map = v_map(obj); paw_Int i = v_int(itr); if (pawH_iter(map, &i)) { - const Value v = *pawH_key(map, cast_size(i)); + const Value v = *pawH_key(map, CAST_SIZE(i)); v_set_int(vm_top(1), i); vm_pushv(v); return PAW_TRUE; @@ -577,7 +564,7 @@ static void eq_ne(paw_Env *P, BinaryOp binop, paw_Type t, Value x, Value y) vm_pop(1); } -#define i2u(i) (cast(i, uint64_t)) +#define i2u(i) (CAST(i, uint64_t)) #define u2i(u) paw_cast_int(u) // Generate code for int operators @@ -636,7 +623,7 @@ static void int_binop(paw_Env *P, BinaryOp binop, paw_Int x, paw_Int y) } else if (y == 0) { z = x; // NOOP } else { - y = paw_min(y, cast(sizeof(x) * 8 - 1, int)); + y = PAW_MIN(y, CAST(sizeof(x) * 8 - 1, int)); z = paw_cast_int(i2u(x) << y); } break; @@ -651,7 +638,7 @@ static void int_binop(paw_Env *P, BinaryOp binop, paw_Int x, paw_Int y) // shift count. If 'x' < 0, then the results of the // shift are implementation-defined (may or may not // preserve the sign). - y = paw_min(y, cast(sizeof(x) * 8 - 1, int)); + y = PAW_MIN(y, CAST(sizeof(x) * 8 - 1, int)); z = x >> y; } } @@ -910,7 +897,7 @@ void pawR_literal_vector(paw_Env *P, int n) StackPtr sp; vm_vector_init(v, sp); if (n > 0) { - pawV_vec_resize(P, v, cast_size(n)); + pawV_vec_resize(P, v, CAST_SIZE(n)); Value *pv = v->end; do { *--pv = *--sp; @@ -1044,21 +1031,21 @@ void pawR_execute(paw_Env *P, CallFrame *cf) { vm_protect(); pawR_literal_tuple(P, get_U(opcode)); - check_gc(P); + CHECK_GC(P); } vm_case(NEWVECTOR) : { vm_protect(); pawR_literal_vector(P, get_U(opcode)); - check_gc(P); + CHECK_GC(P); } vm_case(NEWMAP) : { vm_protect(); pawR_literal_map(P, get_U(opcode)); - check_gc(P); + CHECK_GC(P); } vm_case(CASTBOOL) : @@ -1104,7 +1091,7 @@ void pawR_execute(paw_Env *P, CallFrame *cf) { vm_protect(); new_variant(P, get_A(opcode), get_B(opcode)); - check_gc(P); + CHECK_GC(P); } vm_case(NEWINSTANCE) : @@ -1113,7 +1100,7 @@ void pawR_execute(paw_Env *P, CallFrame *cf) Value *pv = vm_push0(); Instance *ins = pawV_new_instance(P, get_U(opcode)); v_set_object(pv, ins); - check_gc(P); + CHECK_GC(P); } vm_case(INITFIELD) : @@ -1232,7 +1219,7 @@ void pawR_execute(paw_Env *P, CallFrame *cf) ? capture_upvalue(P, base + u.index) : fn->up[u.index]; } - check_gc(P); + CHECK_GC(P); } vm_case(CALL) : diff --git a/src/str.c b/src/str.c index 573190c..c870b80 100644 --- a/src/str.c +++ b/src/str.c @@ -3,10 +3,10 @@ // LICENSE.md. See AUTHORS.md for a list of contributor names. #include "str.h" #include "auxlib.h" -#include "gc_aux.h" +#include "gc.h" #include "mem.h" -#define st_index(st, h) ((h) & (st->capacity - 1)) +#define ST_INDEX(st, h) ((h) & (st->capacity - 1)) static String *new_string(paw_Env *P, size_t length) { @@ -14,7 +14,7 @@ static String *new_string(paw_Env *P, size_t length) pawM_error(P); // size too big for paw_Int } String *str = pawM_new_flex(P, String, length + 1, sizeof(char)); - pawG_add_object(P, cast_object(str), VSTRING); + pawG_add_object(P, CAST_OBJECT(str), VSTRING); str->text[length] = '\0'; str->length = length; str->next = NULL; @@ -39,21 +39,25 @@ static void grow_table(paw_Env *P, StringTable *st) for (size_t i = 0; i < old.capacity; ++i) { for (String *src = old.strings[i]; src;) { String *next = src->next; // save next string - String **pdst = &st->strings[st_index(st, src->hash)]; + String **pdst = &st->strings[ST_INDEX(st, src->hash)]; src->next = *pdst; *pdst = src; src = next; } } pawM_free_vec(P, old.strings, old.capacity); - check_gc(P); + CHECK_GC(P); } -void pawS_init(paw_Env *P) { grow_table(P, &P->strings); } +void pawS_init(paw_Env *P) +{ + grow_table(P, &P->strings); +} void pawS_uninit(paw_Env *P) { StringTable *st = &P->strings; + // strings contained in the table are freed during GC shutdown pawM_free_vec(P, st->strings, st->capacity); st->capacity = 0; st->count = 0; @@ -67,7 +71,7 @@ String *pawS_new_nstr(paw_Env *P, const char *s, size_t n) } const uint32_t hash = pawS_hash(s, n, 0); - String **plist = &st->strings[st_index(st, hash)]; + String **plist = &st->strings[ST_INDEX(st, hash)]; for (String *p = *plist; p; p = p->next) { if (n == p->length && 0 == memcmp(p->text, s, n)) { return p; // already exists @@ -91,7 +95,7 @@ String *pawS_new_str(paw_Env *P, const char *text) String *pawS_new_fixed(paw_Env *P, const char *text) { String *s = pawS_new_str(P, text); - Object *o = cast_object(s); + Object *o = CAST_OBJECT(s); if (o == P->gc_all) { pawG_fix_object(P, o); } @@ -101,7 +105,7 @@ String *pawS_new_fixed(paw_Env *P, const char *text) void pawS_free_str(paw_Env *P, String *s) { StringTable *st = &P->strings; - String **p = &st->strings[st_index(st, s->hash)]; + String **p = &st->strings[ST_INDEX(st, s->hash)]; while (*p != s) { p = &(*p)->next; } diff --git a/src/util.h b/src/util.h index 9b139d8..6f8cdaa 100644 --- a/src/util.h +++ b/src/util.h @@ -16,13 +16,14 @@ #define paw_lengthof(s) (sizeof(s) - 1) #define paw_alignof(x) _Alignof(x) #define paw_countof(a) (sizeof(a) / sizeof((a)[0])) -#define paw_min(x, y) ((x) < (y) ? (x) : (y)) -#define paw_max(x, y) ((x) > (y) ? (x) : (y)) -#define paw_clamp(v, x, y) paw_min(paw_max(v, x), y) +#define PAW_MIN(x, y) ((x) < (y) ? (x) : (y)) +#define PAW_MAX(x, y) ((x) > (y) ? (x) : (y)) +#define PAW_CLAMP(v, x, y) PAW_MIN(PAW_MAX(v, x), y) -#define check_exp(c, e) (paw_assert(c), e) -#define cast(x, t) ((t)(x)) -#define cast_size(x) cast(x, size_t) +#define CHECK_EXP(c, e) (paw_assert(c), e) +#define CAST(x, t) ((t)(x)) +#define CAST_SIZE(x) CAST(x, size_t) +#define CAST_UPTR(x) CAST(x, uintptr_t) // Check for inclusion in one of the character classes #define ISDIGIT(c) (kCharClassTable[(uint8_t)(c)] & 1) @@ -38,7 +39,7 @@ static inline int paw_raw_cmp(void *x, size_t nx, void *y, size_t ny) { - const size_t min = paw_min(nx, ny); + const size_t min = PAW_MIN(nx, ny); const int r = memcmp(x, y, min); if (r == 0) { if (nx < ny) { diff --git a/src/value.c b/src/value.c index d87e6ca..15a73e5 100644 --- a/src/value.c +++ b/src/value.c @@ -3,7 +3,7 @@ // LICENSE.md. See AUTHORS.md for a list of contributor names. #include "prefix.h" -#include "gc_aux.h" +#include "gc.h" #include "map.h" #include "mem.h" #include "rt.h" @@ -34,14 +34,14 @@ static void int_to_string(paw_Env *P, paw_Int i) } else { ++ptr; } - pawC_pushns(P, ptr, cast_size(end - ptr)); + pawC_pushns(P, ptr, CAST_SIZE(end - ptr)); } static void float_to_string(paw_Env *P, paw_Float f) { char temp[32]; const int n = snprintf(temp, paw_countof(temp), "%.*g", 17, f); - pawC_pushns(P, temp, cast_size(n)); + pawC_pushns(P, temp, CAST_SIZE(n)); } const char *pawV_to_string(paw_Env *P, Value v, paw_Type type, size_t *nout) @@ -131,14 +131,14 @@ const char *pawV_name(ValueKind kind) Proto *pawV_new_proto(paw_Env *P) { Proto *p = pawM_new(P, Proto); - pawG_add_object(P, cast_object(p), VPROTO); + pawG_add_object(P, CAST_OBJECT(p), VPROTO); return p; } Type *pawV_new_type(paw_Env *P) { Type *t = pawM_new(P, Type); - pawG_add_object(P, cast_object(t), VTYPE); + pawG_add_object(P, CAST_OBJECT(t), VTYPE); return t; } @@ -155,7 +155,7 @@ void pawV_free_proto(paw_Env *P, Proto *f) UpValue *pawV_new_upvalue(paw_Env *P) { UpValue *u = pawM_new(P, UpValue); - pawG_add_object(P, cast_object(u), VUPVALUE); + pawG_add_object(P, CAST_OBJECT(u), VUPVALUE); return u; } @@ -195,23 +195,23 @@ void pawV_unlink_upvalue(UpValue *u) Tuple *pawV_new_tuple(paw_Env *P, int nelems) { - Tuple *tuple = - pawM_new_flex(P, Tuple, cast_size(nelems), sizeof(tuple->elems[0])); - pawG_add_object(P, cast_object(tuple), VTUPLE); + Tuple *tuple = pawM_new_flex(P, Tuple, CAST_SIZE(nelems), sizeof(tuple->elems[0])); + pawG_add_object(P, CAST_OBJECT(tuple), VTUPLE); + tuple->nelems = nelems; return tuple; } -void pawV_free_tuple(paw_Env *P, Tuple *t, int nelems) +void pawV_free_tuple(paw_Env *P, Tuple *t) { - pawM_free_flex(P, t, nelems, sizeof(t->elems[0])); + pawM_free_flex(P, t, t->nelems, sizeof(t->elems[0])); } Closure *pawV_new_closure(paw_Env *P, int nup) { // Tack on enough space to store 'nup' pointers to UpValue. Closure *f = pawM_new_flex(P, Closure, nup, sizeof(f->up[0])); - pawG_add_object(P, cast_object(f), VCLOSURE); - memset(f->up, 0, cast_size(nup) * sizeof(f->up[0])); + pawG_add_object(P, CAST_OBJECT(f), VCLOSURE); + memset(f->up, 0, CAST_SIZE(nup) * sizeof(f->up[0])); f->nup = nup; return f; } @@ -221,54 +221,31 @@ void pawV_free_closure(paw_Env *P, Closure *f) pawM_free_flex(P, f, f->nup, sizeof(f->up[0])); } -Struct *pawV_new_struct(paw_Env *P, Value *pv) -{ - Struct *struct_ = pawM_new(P, Struct); - v_set_object(pv, struct_); // anchor - pawG_add_object(P, cast_object(struct_), VSTRUCT); - return struct_; -} - -void pawV_free_struct(paw_Env *P, Struct *struct_) { pawM_free(P, struct_); } - Instance *pawV_new_instance(paw_Env *P, int nfields) { - Instance *ins = - pawM_new_flex(P, Instance, cast_size(nfields), sizeof(ins->attrs[0])); - pawG_add_object(P, cast_object(ins), VINSTANCE); + Instance *ins = pawM_new_flex(P, Instance, CAST_SIZE(nfields), sizeof(ins->attrs[0])); + pawG_add_object(P, CAST_OBJECT(ins), VINSTANCE); + ins->nfields = nfields; return ins; } -void pawV_free_instance(paw_Env *P, Instance *ins, int nfields) -{ - pawM_free_flex(P, ins, cast_size(nfields), sizeof(ins->attrs[0])); -} - -Enum *pawV_new_enum(paw_Env *P, int nvariants) +void pawV_free_instance(paw_Env *P, Instance *ins) { - Enum *e = - pawM_new_flex(P, Enum, cast_size(nvariants), sizeof(e->variants[0])); - pawG_add_object(P, cast_object(e), VENUM); - return e; -} - -void pawV_free_enum(paw_Env *P, Enum *e, int nvariants) -{ - pawM_free_flex(P, e, cast_size(nvariants), sizeof(e->variants[0])); + pawM_free_flex(P, ins, CAST_SIZE(ins->nfields), sizeof(ins->attrs[0])); } Variant *pawV_new_variant(paw_Env *P, int k, int nfields) { - Variant *var = - pawM_new_flex(P, Variant, cast_size(nfields), sizeof(var->fields[0])); - pawG_add_object(P, cast_object(var), VVARIANT); + Variant *var = pawM_new_flex(P, Variant, CAST_SIZE(nfields), sizeof(var->fields[0])); + pawG_add_object(P, CAST_OBJECT(var), VVARIANT); + var->nfields = nfields; var->k = k; return var; } -void pawV_free_variant(paw_Env *P, Variant *var, int nfields) +void pawV_free_variant(paw_Env *P, Variant *var) { - pawM_free_flex(P, var, cast_size(nfields), sizeof(var->fields[0])); + pawM_free_flex(P, var, CAST_SIZE(var->nfields), sizeof(var->fields[0])); } Value *pawV_find_attr(Value *attrs, String *name, Type *type) @@ -285,31 +262,37 @@ Value *pawV_find_attr(Value *attrs, String *name, Type *type) static void clear_attrs(Value *pv, int nattrs) { - memset(pv, 0, cast_size(nattrs) * sizeof(*pv)); + memset(pv, 0, CAST_SIZE(nattrs) * sizeof(*pv)); } Method *pawV_new_method(paw_Env *P, Value self, Value call) { Method *mtd = pawM_new(P, Method); - pawG_add_object(P, cast_object(mtd), VMETHOD); + pawG_add_object(P, CAST_OBJECT(mtd), VMETHOD); mtd->self = self; mtd->f = call; return mtd; } -void pawV_free_method(paw_Env *P, Method *m) { pawM_free(P, m); } +void pawV_free_method(paw_Env *P, Method *m) +{ + pawM_free(P, m); +} Native *pawV_new_native(paw_Env *P, paw_Function func, int nup) { // TODO: nup > UINT16_MAX, check it or assert? Native *nat = pawM_new_flex(P, Native, nup, sizeof(nat->up[0])); - pawG_add_object(P, cast_object(nat), VNATIVE); + pawG_add_object(P, CAST_OBJECT(nat), VNATIVE); nat->func = func; nat->nup = nup; return nat; } -void pawV_free_native(paw_Env *P, Native *nat) { pawM_free(P, nat); } +void pawV_free_native(paw_Env *P, Native *f) +{ + pawM_free_flex(P, f, f->nup, sizeof(f->up[0])); +} Foreign *pawV_push_foreign(paw_Env *P, size_t size, int nfields) { @@ -318,8 +301,9 @@ Foreign *pawV_push_foreign(paw_Env *P, size_t size, int nfields) } Value *pv = pawC_push0(P); Foreign *ud = pawM_new_flex(P, Foreign, nfields, sizeof(ud->attrs[0])); - pawG_add_object(P, cast_object(ud), VFOREIGN); + pawG_add_object(P, CAST_OBJECT(ud), VFOREIGN); v_set_object(pv, ud); // anchor + ud->nfields = nfields; ud->size = size; if (size > 0) { // Allocate space to hold 'size' bytes of foreign data. @@ -329,10 +313,10 @@ Foreign *pawV_push_foreign(paw_Env *P, size_t size, int nfields) return ud; } -void pawV_free_foreign(paw_Env *P, Foreign *ud, int nfields) +void pawV_free_foreign(paw_Env *P, Foreign *ud) { pawM_free_vec(P, (char *)ud->data, ud->size); // TODO - pawM_free_flex(P, ud, cast_size(nfields), sizeof(ud->attrs[0])); + pawM_free_flex(P, ud, CAST_SIZE(ud->nfields), sizeof(ud->attrs[0])); } paw_Bool pawV_truthy(Value v, paw_Type type) @@ -450,7 +434,7 @@ int pawV_parse_uint64(paw_Env *P, const char *text) if (p[0] == '0') { if ((rc = char2base(p[1])) > 0) { p += 2; // skip base prefix - base = cast(rc, unsigned); + base = CAST(rc, unsigned); } else if (p[1] == '\0') { pawC_pushi(P, 0); return PAW_OK; diff --git a/src/value.h b/src/value.h index 32ff2f0..a5321cb 100644 --- a/src/value.h +++ b/src/value.h @@ -56,23 +56,22 @@ #define o_is_method(o) (o_kind(o) == VMETHOD) #define o_is_foreign(o) (o_kind(o) == VFOREIGN) -#define o_string(o) check_exp(o_is_string(o), (String *)(o)) -#define o_native(o) check_exp(o_is_native(o), (Native *)(o)) -#define o_proto(o) check_exp(o_is_proto(o), (Proto *)(o)) -#define o_closure(o) check_exp(o_is_closure(o), (Closure *)(o)) -#define o_upvalue(o) check_exp(o_is_upvalue(o), (UpValue *)(o)) -#define o_map(o) check_exp(o_is_map(o), (Map *)(o)) -#define o_vector(o) check_exp(o_is_vector(o), (Vector *)(o)) -#define o_tuple(o) check_exp(o_is_tuple(o), (Tuple *)(o)) -#define o_variant(o) check_exp(o_is_variant(o), (Variant *)(o)) -#define o_enum(o) check_exp(o_is_enum(o), (Enum *)(o)) -#define o_instance(o) check_exp(o_is_instance(o), (Instance *)(o)) -#define o_struct(o) check_exp(o_is_struct(o), (Struct *)(o)) -#define o_method(o) check_exp(o_is_method(o), (Method *)(o)) -#define o_foreign(o) check_exp(o_is_foreign(o), (Foreign *)(o)) - -#define cast_uintptr(x) ((uintptr_t)(x)) -#define cast_object(x) ((Object *)(void *)(x)) +#define o_string(o) CHECK_EXP(o_is_string(o), (String *)(o)) +#define o_native(o) CHECK_EXP(o_is_native(o), (Native *)(o)) +#define o_proto(o) CHECK_EXP(o_is_proto(o), (Proto *)(o)) +#define o_closure(o) CHECK_EXP(o_is_closure(o), (Closure *)(o)) +#define o_upvalue(o) CHECK_EXP(o_is_upvalue(o), (UpValue *)(o)) +#define o_map(o) CHECK_EXP(o_is_map(o), (Map *)(o)) +#define o_vector(o) CHECK_EXP(o_is_vector(o), (Vector *)(o)) +#define o_tuple(o) CHECK_EXP(o_is_tuple(o), (Tuple *)(o)) +#define o_variant(o) CHECK_EXP(o_is_variant(o), (Variant *)(o)) +#define o_enum(o) CHECK_EXP(o_is_enum(o), (Enum *)(o)) +#define o_instance(o) CHECK_EXP(o_is_instance(o), (Instance *)(o)) +#define o_struct(o) CHECK_EXP(o_is_struct(o), (Struct *)(o)) +#define o_method(o) CHECK_EXP(o_is_method(o), (Method *)(o)) +#define o_foreign(o) CHECK_EXP(o_is_foreign(o), (Foreign *)(o)) + +#define CAST_OBJECT(x) (CAST(CAST(x, void *), Object *)) typedef enum ValueKind { // scalar types @@ -104,10 +103,10 @@ typedef enum ValueKind { NVTYPES } ValueKind; -#define GC_HEADER \ +#define GC_HEADER \ struct Object *gc_next; \ - uint64_t gc_nrefs; \ - ValueKind gc_kind : 8 + uint8_t gc_mark : 2; \ + ValueKind gc_kind : 6 typedef struct Object { GC_HEADER; } Object; @@ -172,10 +171,10 @@ static paw_Int pawV_abs_index(paw_Int index, size_t length) static inline size_t pawV_check_abs(paw_Env *P, paw_Int index, size_t length) { index = pawV_abs_index(index, length); - if (index < 0 || cast_size(index) >= length) { + if (index < 0 || CAST_SIZE(index) >= length) { pawV_index_error(P, index, length); } - return cast_size(index); + return CAST_SIZE(index); } // Convert a null-terminated string into a 64-bit unsigned integer @@ -208,6 +207,7 @@ typedef struct Proto { GC_HEADER; uint8_t is_va; + Object *gc_list; String *name; String *modname; uint32_t *source; @@ -227,7 +227,6 @@ typedef struct Proto { struct Proto **p; // nested functions int nup; // number of upvalues int nlines; // number of lines - int ndebug; // number of locals int nk; // number of constants int argc; // number of fixed parameters int nproto; // number of nested functions @@ -255,12 +254,13 @@ void pawV_link_upvalue(paw_Env *P, UpValue *u, UpValue *prev, UpValue *next); void pawV_unlink_upvalue(UpValue *u); #define upv_is_open(up) ((up)->p.p != &(up)->closed) -#define upv_level(up) check_exp(upv_is_open(up), (StackPtr)((up)->p.p)) +#define upv_level(up) CHECK_EXP(upv_is_open(up), (StackPtr)((up)->p.p)) typedef struct Closure { GC_HEADER; uint16_t nup; Proto *p; + Object *gc_list; UpValue *up[]; } Closure; @@ -270,21 +270,27 @@ void pawV_free_closure(paw_Env *P, Closure *c); typedef struct Native { GC_HEADER; uint16_t nup; + Object *gc_list; paw_Function func; Value up[]; } Native; Native *pawV_new_native(paw_Env *P, paw_Function func, int nup); +void pawV_free_native(paw_Env *P, Native *f); typedef struct Tuple { GC_HEADER; + int nelems; + Object *gc_list; Value elems[]; } Tuple; Tuple *pawV_new_tuple(paw_Env *P, int nelems); +void pawV_free_tuple(paw_Env *P, Tuple *t); typedef struct Vector { GC_HEADER; + Object *gc_list; Value *begin; Value *end; Value *upper; @@ -303,12 +309,12 @@ void pawV_vec_pop(paw_Env *P, Vector *vec, paw_Int index); static inline size_t pawV_vec_length(const Vector *vec) { - return cast_size(vec->end - vec->begin); + return CAST_SIZE(vec->end - vec->begin); } static inline Value *pawV_vec_get(paw_Env *P, Vector *vec, paw_Int index) { - const paw_Int abs = pawV_abs_index(index, cast_size(vec->end - vec->begin)); + const paw_Int abs = pawV_abs_index(index, CAST_SIZE(vec->end - vec->begin)); const size_t i = pawV_check_abs(P, abs, pawV_vec_length(vec)); return &vec->begin[i]; } @@ -330,49 +336,39 @@ typedef struct MapMeta { typedef struct Map { GC_HEADER; + Object *gc_list; void *data; size_t length; size_t capacity; } Map; -typedef struct Struct { - GC_HEADER; // common members for GC - paw_Type type; // index in module type list -} Struct; - -Struct *pawV_new_struct(paw_Env *P, Value *pv); -void pawV_free_struct(paw_Env *P, Struct *struct_); - // Instance of a struct typedef struct Instance { GC_HEADER; // common members for GC + int nfields; + Object *gc_list; Value attrs[]; // fixed array of attributes // Value fields[]; // data fields } Instance; Instance *pawV_new_instance(paw_Env *P, int nfields); -void pawV_free_instance(paw_Env *P, Instance *ins, int nfields); - -typedef struct Enum { - GC_HEADER; // common members for GC - Value variants[]; // enumerators -} Enum; - -Enum *pawV_new_enum(paw_Env *P, int nvariants); -void pawV_free_enum(paw_Env *P, Enum *e, int nvariants); +void pawV_free_instance(paw_Env *P, Instance *ins); typedef struct Variant { GC_HEADER; // common members for GC uint8_t k; // discriminator + int nfields; + Object *gc_list; Value fields[]; // data fields } Variant; Variant *pawV_new_variant(paw_Env *P, int k, int nfields); -void pawV_free_variant(paw_Env *P, Variant *var, int nfields); +void pawV_free_variant(paw_Env *P, Variant *var); // Method bound to an instance typedef struct Method { GC_HEADER; + Object *gc_list; Value self; Value f; } Method; @@ -386,12 +382,14 @@ void pawV_free_method(paw_Env *P, Method *); typedef struct Foreign { GC_HEADER; uint8_t flags; + int nfields; + Object *gc_list; void *data; size_t size; - Value attrs[]; // fixed array of attributes + Value attrs[]; } Foreign; Foreign *pawV_push_foreign(paw_Env *P, size_t size, int nfields); -void pawV_free_foreign(paw_Env *P, Foreign *ud, int nfields); +void pawV_free_foreign(paw_Env *P, Foreign *ud); #endif // PAW_VALUE_H diff --git a/src/vector.c b/src/vector.c index 5cd2263..731d0ec 100644 --- a/src/vector.c +++ b/src/vector.c @@ -1,7 +1,8 @@ // Copyright (c) 2024, The paw Authors. All rights reserved. // This source code is licensed under the MIT License, which can be found in // LICENSE.md. See AUTHORS.md for a list of contributor names. -#include "gc_aux.h" + +#include "gc.h" #include "mem.h" #include "rt.h" #include "util.h" @@ -16,7 +17,7 @@ void pawV_index_error(paw_Env *P, paw_Int index, size_t length) static size_t vector_capacity(const Vector *a) { - return cast_size(a->upper - a->begin); + return CAST_SIZE(a->upper - a->begin); } static void realloc_vector(paw_Env *P, Vector *a, size_t alloc0, size_t alloc) @@ -25,7 +26,7 @@ static void realloc_vector(paw_Env *P, Vector *a, size_t alloc0, size_t alloc) pawM_resize(P, a->begin, alloc0, alloc); a->end = a->begin + end; a->upper = a->begin + alloc; - check_gc(P); + CHECK_GC(P); } static void ensure_space(paw_Env *P, Vector *a, size_t have, size_t want) @@ -44,7 +45,7 @@ static void ensure_space(paw_Env *P, Vector *a, size_t have, size_t want) static void reserve_extra(paw_Env *P, Vector *a, size_t extra) { paw_assert(extra > 0); - if (extra <= cast_size(a->upper - a->end)) { + if (extra <= CAST_SIZE(a->upper - a->end)) { return; // Still have enough space } const size_t have = vector_capacity(a); @@ -53,7 +54,7 @@ static void reserve_extra(paw_Env *P, Vector *a, size_t extra) static void move_items(Value *src, ptrdiff_t shift, size_t count) { - memmove(src + shift, src, cast_size(count) * sizeof(src[0])); + memmove(src + shift, src, CAST_SIZE(count) * sizeof(src[0])); } void pawV_vec_reserve(paw_Env *P, Vector *a, size_t want) @@ -83,7 +84,7 @@ void pawV_vec_insert(paw_Env *P, Vector *a, paw_Int index, Value v) // Clamp to the vector bounds. const size_t len = pawV_vec_length(a); const paw_Int abs = pawV_abs_index(index, len); - const size_t i = paw_clamp(cast_size(abs), 0, len); + const size_t i = PAW_CLAMP(CAST_SIZE(abs), 0, len); reserve_extra(P, a, 1); if (i != len) { @@ -108,7 +109,7 @@ void pawV_vec_pop(paw_Env *P, Vector *a, paw_Int index) Vector *pawV_vec_new(paw_Env *P) { Vector *a = pawM_new(P, Vector); - pawG_add_object(P, cast_object(a), VVECTOR); + pawG_add_object(P, CAST_OBJECT(a), VVECTOR); return a; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c5d1868..2f945b0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,6 +20,7 @@ function(test_script NAME ARGS) endfunction() build_test(test_impl) +build_test(test_alloc) build_test(test_rt) build_test(test_oom) build_test(test_so) diff --git a/test/scripts/poly_function.paw b/test/scripts/poly_function.paw index 4636c83..72b8b7b 100644 --- a/test/scripts/poly_function.paw +++ b/test/scripts/poly_function.paw @@ -30,9 +30,8 @@ fn second_recursive(x: X, y: Y, z: Z) { } pub fn test_recursive_instantiation() { - first_recursive(1, 2.0, 'three') - second_recursive('four', 5.0, 6) +// first_recursive(1, 2.0, 'three') +// second_recursive('four', 5.0, 6) second_recursive(|| -> int {return 42}, [f2i], map::) } - diff --git a/test/scripts/primitive.paw b/test/scripts/primitive.paw index 0c495c1..bedfed2 100644 --- a/test/scripts/primitive.paw +++ b/test/scripts/primitive.paw @@ -84,3 +84,18 @@ fn test_conversions() { let i = b as int assert(i == 1) } + +fn test_lots_of_strings() { + let alphabet = 'abcdefghijklmnopqrstuvwxyz' + + // put strings in a vector so they don't get collected + let v = [] + for i = 0, #alphabet { + for j = 0, #alphabet { + let s = alphabet[:i] + alphabet[j:] + + alphabet[i:] + alphabet[:j] + _vector_push(v, s) + } + } +} + diff --git a/test/test.c b/test/test.c index f2408b3..77eae73 100644 --- a/test/test.c +++ b/test/test.c @@ -14,46 +14,6 @@ #include #include -#include - -#if 0 -#define TEST_FIND_LEAK -// Define TEST_FIND_LEAK to have the program print out the addresses and -// sizes of leaked blocks. Watchpoints can be used to figure out exactly -// where the block was allocated. Note that the tests are very slow with -// this option enabled. -#ifdef TEST_FIND_LEAK -struct Chunk { - void *data; - size_t size; -}; - -static struct { - struct Chunk *blocks; - size_t size; - size_t alloc; -} s_chunks; - -static struct Chunk *get_chunk(void *ptr) -{ - if (s_chunks.size == s_chunks.alloc) { - s_chunks.alloc = paw_max(s_chunks.alloc * 2, 256); - s_chunks.blocks = realloc(s_chunks.blocks, s_chunks.alloc * sizeof(s_chunks.blocks[0])); - } - for (size_t i = 0; i < s_chunks.size; ++i) { - struct Chunk *c = &s_chunks.blocks[i]; - if (ptr == c->data) { - return c; - } - } - struct Chunk *c = &s_chunks.blocks[s_chunks.size++]; - *c = (struct Chunk){ - .data = ptr, - }; - return c; -} -#endif // TEST_FIND_LEAK - static unsigned *get_block(struct TestAlloc *a, size_t size) { if (size < BLOCK_LIMIT) { @@ -61,7 +21,7 @@ static unsigned *get_block(struct TestAlloc *a, size_t size) } // Chunks past BLOCK_LIMIT represent BLOCK_LIMIT bytes each, except for the last block, // which represents all allocations greater than the preceeding block. Block 0 is unused. - size = paw_min(size / BLOCK_LIMIT, BLOCK_LIMIT); + size = PAW_MIN(size / BLOCK_LIMIT, BLOCK_LIMIT); return &a->blocks[size + BLOCK_LIMIT]; } @@ -82,15 +42,6 @@ static void register_block(struct TestAlloc *a, size_t size0, size_t size) static void report_nonzero_blocks(struct TestAlloc *a) { size_t nonzero = 0; -#ifdef TEST_FIND_LEAK - // Print the exact pointers that were leaked. - for (size_t i = 0; i < s_chunks.size; ++i) { - struct Chunk c = s_chunks.blocks[i]; - if (c.size) { - fprintf(stderr, "leaked %zu bytes at address %p\n", c.size, c.data); - } - } -#else // Just give a rough indication of what size blocks were leaked. unsigned *block = a->blocks; for (size_t i = 0; i < BLOCK_LIMIT; ++i, ++block) { @@ -110,14 +61,12 @@ static void report_nonzero_blocks(struct TestAlloc *a) fprintf(stderr, "leaked %u large block(s)\n", *block); ++nonzero; } -#endif // !TEST_FIND_LEAK if (nonzero) { fprintf(stderr, "leaked %zu allocations (%zu bytes total)\n", nonzero, a->nbytes); abort(); } } -#endif // 0 static void trash_memory(void *ptr, size_t n) { @@ -129,39 +78,30 @@ static void trash_memory(void *ptr, size_t n) static void *safe_realloc(struct TestAlloc *a, void *ptr, size_t size0, size_t size) { + check((size0 == 0) == (ptr == NULL)); check(a->nbytes >= size0); - // register_block(a, size0, size); - void *ptr2 = size ? GC_MALLOC(size) : NULL; - check(!size || ptr2); // assume success - if (ptr2) { - if (ptr) { + void *ptr2 = size ? malloc(size) : NULL; + check(size == 0 || ptr2 != NULL); // assume success + if (ptr2 != NULL) { + if (ptr != NULL) { // resize: copy old contents - memcpy(ptr2, ptr, paw_min(size0, size)); + memcpy(ptr2, ptr, PAW_MIN(size0, size)); } if (size0 < size) { // grow: fill uninitialized memory trash_memory((char *)ptr2 + size0, size - size0); } } -#ifdef TEST_FIND_LEAK - if (ptr) { - struct Chunk *c = get_chunk(ptr); - check(c->size == size0); - c->size = 0; + if (ptr != NULL || ptr2 != NULL) { + register_block(a, size0, size); } - if (ptr2) { - struct Chunk *c = get_chunk(ptr2); - check(c->size == 0); - c->size = size; - } -#endif - if (ptr) { + if (ptr != NULL) { // Trash the old allocation in an attempt to mess up any code // that still depends on it. trash_memory(ptr, size0); } a->nbytes += size - size0; - GC_FREE(ptr); + free(ptr); return ptr2; } @@ -178,7 +118,7 @@ static void next_chunk(struct TestReader *rd) rd->length = pawO_read(rd->file, rd->buf, sizeof(rd->buf)); return; } - const size_t n = paw_min(rd->ndata, READ_MAX); + const size_t n = PAW_MIN(rd->ndata, READ_MAX); memcpy(rd->buf, rd->data, n); rd->data += n; rd->ndata -= n; @@ -197,7 +137,7 @@ const char *test_reader(paw_Env *P, void *ud, size_t *size) } } const size_t r = (size_t)rand(); - *size = paw_max(1, r % rd->length); + *size = PAW_MAX(1, r % rd->length); const char *ptr = rd->buf + rd->index; rd->length -= *size; rd->index += *size; @@ -224,11 +164,11 @@ void test_close(paw_Env *P, struct TestAlloc *a) { paw_close(P); - // if (a->nbytes) { - // fprintf(stderr, "error: leaked %zu bytes\n", a->nbytes); - // report_nonzero_blocks(a); - // abort(); - // } + if (a->nbytes > 0) { + fprintf(stderr, "error: leaked %zu bytes\n", a->nbytes); + report_nonzero_blocks(a); + abort(); + } } static void check_ok(paw_Env *P, int status) diff --git a/test/test_alloc.c b/test/test_alloc.c new file mode 100644 index 0000000..8861344 --- /dev/null +++ b/test/test_alloc.c @@ -0,0 +1,118 @@ +// Copyright (c) 2024, The paw Authors. All rights reserved. +// This source code is licensed under the MIT License, which can be found in +// LICENSE.md. See AUTHORS.md for a list of contributor names. + +#include "alloc.h" +#include "test.h" +#include "util.h" + +static void driver(void (*test_callback)(paw_Env *P)) +{ + struct TestAlloc a = {0}; + paw_Env *P = test_open(test_alloc, &a); + test_callback(P); + test_close(P, &a); +} + +static void open_and_close(paw_Env *P) +{ + paw_unused(P); +} + +static void alloc_and_free(paw_Env *P, size_t size) +{ + void *ptr = pawZ_alloc(P, NULL, 0, size); + pawZ_alloc(P, ptr, size, 0); +} + +#define MAX_DEFER 16384 +static struct DeferredAlloc { + void *ptr; + size_t size; +} s_defer[MAX_DEFER]; +static int s_ndefer = 0; + +static void set_defer_data(void *ptr, size_t size) +{ + memset(ptr, size & 255, size); +} + +static void check_defer_data(const void *ptr, size_t size) +{ + const uint8_t *data = ptr; + for (size_t i = 0; i < size; ++i) { + check(data[i] == CAST(size & 255, uint8_t)); + } +} + +static void alloc_and_defer(paw_Env *P, size_t size) +{ + check(s_ndefer < MAX_DEFER); + void *ptr = pawZ_alloc(P, NULL, 0, size); + const int index = s_ndefer++; + s_defer[index] = (struct DeferredAlloc){ + .size = size, + .ptr = ptr, + }; + set_defer_data(ptr, size); +} + +static void free_deferred_ptrs(paw_Env *P) +{ + while (s_ndefer > 0) { + struct DeferredAlloc defer = s_defer[--s_ndefer]; + check_defer_data(defer.ptr, defer.size); + pawZ_alloc(P, defer.ptr, defer.size, 0); + } +} + +static void alloc_pattern(paw_Env *P, size_t size) +{ + alloc_and_defer(P, size + 10); + alloc_and_defer(P, size + 11); + alloc_and_defer(P, size + 12); + alloc_and_defer(P, size + 13); + alloc_and_free(P, size + 1); + alloc_and_defer(P, size + 14); + alloc_and_defer(P, size + 15); + alloc_and_defer(P, size + 16); + alloc_and_free(P, size + 2); + alloc_and_free(P, size + 3); + alloc_and_defer(P, size + 17); + alloc_and_defer(P, size + 18); + alloc_and_free(P, size + 4); + alloc_and_free(P, size + 5); + alloc_and_free(P, size + 6); + alloc_and_defer(P, size + 19); + alloc_and_free(P, size + 7); + alloc_and_free(P, size + 8); + alloc_and_free(P, size + 9); +} + +static void test_small_allocations(paw_Env *P) +{ + const size_t sizes[] = {0, 10, 11, 100, 101, 102}; + for (size_t i = 0; i < paw_countof(sizes); ++i) { + alloc_pattern(P, sizes[i]); + alloc_pattern(P, sizes[i]); + } + free_deferred_ptrs(P); +} + +static void test_lots_of_allocations(paw_Env *P) +{ + for (size_t i = 0; i < 1000; ++i) { + alloc_pattern(P, CAST_SIZE(rand() % 100 + 1)); + } + for (size_t i = 0; i < 100; ++i) { + alloc_pattern(P, CAST_SIZE(rand() % 10000 + 1)); + } + free_deferred_ptrs(P); +} + +int main(void) +{ + driver(open_and_close); + driver(test_small_allocations); + driver(test_lots_of_allocations); +} diff --git a/test/test_error.c b/test/test_error.c index fea5bab..526445b 100644 --- a/test/test_error.c +++ b/test/test_error.c @@ -7,7 +7,6 @@ #include "test.h" #include -static struct TestAlloc s_alloc; typedef uint64_t TypeSet; static void write_main(char *out, const char *items, const char *text) @@ -29,11 +28,11 @@ static void test_compiler_error(int expect, const char *name, const char *item, char buffer[4096]; write_main(buffer, item, text); - paw_Env *P = test_open(NULL, &s_alloc); + paw_Env *P = paw_open(NULL, NULL); int status = pawL_load_chunk(P, name, buffer); check(status == expect); - test_close(P, &s_alloc); + paw_close(P); } static void test_runtime_error(int expect, const char *name, const char *item, const char *text) @@ -41,7 +40,7 @@ static void test_runtime_error(int expect, const char *name, const char *item, c char buffer[4096]; write_main(buffer, item, text); - paw_Env *P = test_open(NULL, &s_alloc); + paw_Env *P = paw_open(NULL, NULL); int status = pawL_load_chunk(P, name, buffer); check(status == PAW_OK); @@ -52,7 +51,7 @@ static void test_runtime_error(int expect, const char *name, const char *item, c status = paw_call(P, 0); check(status == expect); - test_close(P, &s_alloc); + paw_close(P); } static void test_name_error(void) diff --git a/test/test_impl.c b/test/test_impl.c index b938f56..8967582 100644 --- a/test/test_impl.c +++ b/test/test_impl.c @@ -162,7 +162,7 @@ static void test_map3(paw_Env *P) map_put(P, m, known[i], known[i]); } - check(cast_size(paw_length(P, -1)) == paw_countof(known)); + check(CAST_SIZE(paw_length(P, -1)) == paw_countof(known)); // Fill the map with nonnegative integers (may have repeats). paw_Int integers[N]; @@ -172,18 +172,18 @@ static void test_map3(paw_Env *P) integers[i] = ival; } - check(cast_size(paw_length(P, -1)) <= N + paw_countof(known)); + check(CAST_SIZE(paw_length(P, -1)) <= N + paw_countof(known)); // Erase all nonnegative integers. paw_Int itr = PAW_ITER_INIT; while (pawH_iter(m, &itr)) { - const Value key = *pawH_key(m, cast_size(itr)); + const Value key = *pawH_key(m, CAST_SIZE(itr)); if (v_int(key) >= 0) { map_del(P, m, key.i); } } - check(cast_size(pawH_length(m)) <= paw_countof(known)); + check(CAST_SIZE(pawH_length(m)) <= paw_countof(known)); // Check known items. for (size_t i = 0; i < paw_countof(known); ++i) { @@ -194,6 +194,33 @@ static void test_map3(paw_Env *P) map_free(P, m); } +static void test_strings(paw_Env *P) +{ + paw_push_nstring(P, "fixed\0\1", 7); + const void *fixed = P->top.p[-1].p; + + int total_words = 0; + const char data[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for (size_t wordlen = 1; wordlen < 26; ++wordlen) { + const size_t nwords = paw_lengthof(data) - wordlen; + for (size_t i = 0; i < nwords; ++i) { + paw_push_nstring(P, data + i, wordlen); + ++total_words; + } + if (total_words > 50) { + const int npop = total_words / 3; + total_words -= npop; + paw_pop(P, npop); + } + } + printf("total %d\n",total_words); + + paw_push_nstring(P, "fixed\0\1", 7); + check(fixed == P->top.p[-1].p); +} + static void test_stack(paw_Env *P) { paw_push_unit(P, 2); @@ -212,7 +239,7 @@ static void driver(void (*callback)(paw_Env *)) static void parse_int(paw_Env *P, void *ud) { - paw_push_string(P, cast(ud, const char *)); + paw_push_string(P, CAST(ud, const char *)); pawR_to_int(P, PAW_TSTRING); } @@ -231,6 +258,7 @@ static void test_parse_int(paw_Env *P) int main(void) { test_primitives(); + driver(test_strings); driver(test_stack); driver(test_map1); driver(test_map2); diff --git a/test/test_oom.c b/test/test_oom.c index e595119..26b2d44 100644 --- a/test/test_oom.c +++ b/test/test_oom.c @@ -1,4 +1,6 @@ #include "test.h" +#include "call.h" +#include "env.h" void *oom_alloc(void *ud, void *ptr, size_t size0, size_t size) { @@ -11,17 +13,29 @@ void *oom_alloc(void *ud, void *ptr, size_t size0, size_t size) return test_alloc(ud, ptr, size0, size); } +void run_tests(paw_Env *P) +{ + struct GlobalVec *gvec = &P->gv; + for (int i = 0; i < gvec->size; ++i) { + static const char kPrefix[] = "test_"; + static const size_t kLength = paw_lengthof(kPrefix); + struct GlobalVar *gvar = &gvec->data[i]; + if (gvar->name->length >= kLength && + 0 == memcmp(gvar->name->text, kPrefix, kLength)) { + printf("running oom.%s...\n", gvar->name->text); + pawC_pushv(P, gvar->value); + check(PAW_OK == paw_call(P, 0)); + } + } +} + static int script_aux(const char *name, struct TestAlloc *a) { paw_Env *P = paw_open(oom_alloc, a); - if (!P) { - return PAW_EMEMORY; - } + if (P == NULL) return PAW_EMEMORY; + int status = test_open_file(P, name); - if (status == PAW_OK) { - // run the module - status = paw_call(P, 0); - } + if (status == PAW_OK) run_tests(P); test_close(P, a); return status; } diff --git a/test/test_rt.c b/test/test_rt.c index d7087a7..3a7c98c 100644 --- a/test/test_rt.c +++ b/test/test_rt.c @@ -1,3 +1,7 @@ +// Copyright (c) 2024, The paw Authors. All rights reserved. +// This source code is licensed under the MIT License, which can be found in +// LICENSE.md. See AUTHORS.md for a list of contributor names. + #include "call.h" #include "env.h" #include "test.h" @@ -38,8 +42,6 @@ int main(void) script("block"); script("loop"); script("operator"); - script("struct"); - script("vector"); script("map"); script("bubble"); script("mandelbrot");