Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399#include "silver/code_unit.h"#include "silver/engine.h"
#include "cage.h"#include "runtime.h"#include "gc/roots.h"#include "shapes.h"#include "utils.h"
#include <stdlib.h>#include <string.h>
static constexpr size_t SV_CODE_BLOCK_SIZE = 16 * 1024;static constexpr uint32_t SV_CODE_SPARE_MAX = 64;
struct sv_code_block { sv_code_block_t *next; size_t used; size_t capacity; size_t alloc_size; uint32_t live_units; alignas(CODE_ARENA_ALIGNMENT) char data[];};
static sv_code_block_t *block_take(sv_code_units_t *u, size_t size) { const size_t block_size = SV_CODE_BLOCK_SIZE; if (size <= block_size - offsetof(sv_code_block_t, data) && u->spare) { sv_code_block_t *block = u->spare; u->spare = block->next; u->spare_count--; block->next = NULL; u->held_bytes += block->alloc_size; return block; }
size_t alloc_size = block_size; size_t need = offsetof(sv_code_block_t, data) + size; if (need > block_size) alloc_size = (need + block_size - 1) / block_size * block_size; sv_code_block_t *block = ant_cage_alloc(alloc_size, _Alignof(sv_code_block_t)); if (!block) return NULL;
block->next = NULL; block->used = 0; block->capacity = alloc_size - offsetof(sv_code_block_t, data); block->alloc_size = alloc_size; block->live_units = 0; u->held_bytes += alloc_size; return block;}
static void block_release(sv_code_units_t *u, sv_code_block_t *block) { u->held_bytes -= block->alloc_size; if (block->alloc_size == SV_CODE_BLOCK_SIZE && u->spare_count < SV_CODE_SPARE_MAX) { memset(block->data, 0, block->used); block->used = 0; block->next = u->spare; u->spare = block; u->spare_count++; return; } ant_cage_free(block, block->alloc_size);}
void *sv_code_unit_bump(sv_code_unit_t *unit, size_t size) { if (!unit) return NULL; if (size == 0) size = 1;
sv_code_units_t *u = &unit->js->code_units; const size_t mask = (size_t)CODE_ARENA_ALIGNMENT - 1u; size = (size + mask) & ~mask;
sv_code_block_t *block = u->current; if (!block || block->used + size > block->capacity) { sv_code_block_t *fresh = block_take(u, size); if (!fresh) return NULL; if (fresh->alloc_size == SV_CODE_BLOCK_SIZE) { if (block && block->live_units == 0) block_release(u, block); u->current = fresh; } block = fresh; }
if (unit->block_count == 0 || unit->blocks[unit->block_count - 1] != block) { sv_code_block_t **blocks = vec_grow(unit->blocks, &unit->block_cap, unit->block_count + 1, sizeof(*blocks), 8); if (!blocks) { if (block != u->current) block_release(u, block); return NULL; } unit->blocks = blocks; unit->blocks[unit->block_count++] = block; block->live_units++; }
void *ptr = block->data + block->used; block->used += size; unit->js->gc.code_alloc += size; return ptr;}
void *sv_code_bump(ant_t *js, size_t size) { sv_code_unit_t *unit = js->code_units.active; return unit ? sv_code_unit_bump(unit, size) : code_arena_bump(js, size);}
const char *sv_code_text(ant_t *js, const char *code, size_t len) { sv_code_unit_t *unit = js->code_units.active; if (!unit) return code_arena_alloc(js, code, len); if (!code || len == 0) return NULL; char *dest = sv_code_unit_bump(unit, len + 1); if (!dest) return NULL; memcpy(dest, code, len); dest[len] = '\0'; return dest;}
void sv_code_unit_pin(sv_code_unit_t *unit) { if (!unit || unit->pins++) return; sv_code_units_t *u = &unit->js->code_units; unit->pinned_prev = NULL; unit->pinned_next = u->pinned; if (u->pinned) u->pinned->pinned_prev = unit; u->pinned = unit;}
void sv_code_unit_unpin(sv_code_unit_t *unit) { if (!unit || !unit->pins || --unit->pins) return; sv_code_units_t *u = &unit->js->code_units; if (unit->pinned_prev) unit->pinned_prev->pinned_next = unit->pinned_next; else u->pinned = unit->pinned_next; if (unit->pinned_next) unit->pinned_next->pinned_prev = unit->pinned_prev; unit->pinned_prev = unit->pinned_next = NULL;}
sv_code_unit_t *sv_code_unit_begin(ant_t *js) { sv_code_unit_t *unit = calloc(1, sizeof(*unit)); if (!unit) return NULL;
unit->js = js; sv_code_unit_pin(unit); unit->compiling = true; js->code_units.held_bytes += SV_CODE_UNIT_OVERHEAD; js->gc.code_alloc += SV_CODE_UNIT_OVERHEAD; unit->outer = js->code_units.active; unit->next = js->code_units.units; js->code_units.units = unit; js->code_units.unit_count++; js->code_units.active = unit; return unit;}
void sv_code_unit_finish(sv_code_unit_t *unit) { if (!unit) return; unit->js->code_units.active = unit->outer; unit->outer = NULL; unit->compiling = false; free(unit->compile_roots); unit->compile_roots = NULL; unit->compile_root_count = unit->compile_root_cap = 0;
ant_t *js = unit->js; if (js->gc.code_alloc >= gc_code_major_threshold(js)) gc_run(js);}
void sv_code_unit_add_func(ant_t *js, sv_func_t *func) { if (!func) return; sv_code_unit_t *unit = js->code_units.active; sv_func_t **head = unit ? &unit->funcs : &js->code_units.unowned_funcs; func->unit = unit; func->unit_next = *head; *head = func;}
bool sv_code_unit_root(ant_t *js, ant_value_t value) { sv_code_unit_t *unit = js->code_units.active; if (!unit) return true; ant_value_t *roots = vec_grow( unit->compile_roots, &unit->compile_root_cap, unit->compile_root_count + 1, sizeof(*roots), 8 ); if (!roots) return false; unit->compile_roots = roots; unit->compile_roots[unit->compile_root_count++] = value; return true;}
bool sv_code_unit_retain_template(ant_t *js, sv_func_t *func, ant_value_t value) { if (!func || !func->unit) return gc_pin_permanent(js, value);
sv_code_units_t *u = &js->code_units; ant_value_t *young = vec_grow( u->young_values, &u->young_cap, u->young_len + 1, sizeof(*young), 64 ); if (!young) return false; u->young_values = young; u->young_values[u->young_len++] = value; return true;}
void sv_code_unit_make_immortal(sv_func_t *func) { if (func && func->unit) func->unit->immortal = true;}
bool sv_code_units_watch_feedback(sv_func_t *func, sv_func_t *callee) { if (func->fb_unit_watched) return true; sv_code_units_t *u = &callee->unit->js->code_units; sv_func_t **watch = vec_grow( u->fb_watch, &u->fb_watch_cap, u->fb_watch_len + 1, sizeof(*watch), 64 ); if (!watch) return false; u->fb_watch = watch; u->fb_watch[u->fb_watch_len++] = func; func->fb_unit_watched = true; return true;}
static void func_free_sidecar(sv_func_t *func) { uintptr_t raw = (uintptr_t)func->type_feedback; if (!(raw & ant_sidecar)) return; sv_func_sidecar_t *sidecar = (sv_func_sidecar_t *)(raw & ~ant_sidecar); free(sidecar->type_feedback); free(sidecar->elem_types); free(sidecar->jit_await_map); sv_try_table_free(sidecar->try_table); free(sidecar); func->type_feedback = NULL;}
static void func_release(ant_t *js, sv_func_t *func) { uintptr_t raw = (uintptr_t)func->type_feedback; if (raw & ant_sidecar) { sv_func_sidecar_t *sidecar = (sv_func_sidecar_t *)(raw & ~ant_sidecar); if (sidecar->ctor_proto_shape_registered) { ant_shape_t *shape = sidecar->ctor_proto_shape; sidecar->ctor_proto_shape = SV_IC_SHAPE_SLOT_DEAD; if (shape) ant_shape_release(shape); } } else free(func->type_feedback);
free(func->local_type_feedback); free(func->call_target_fb); if (!(raw & ant_sidecar)) func->type_feedback = NULL; func->local_type_feedback = NULL; func->call_target_fb = NULL;
for (uint32_t i = 0; func->obj_sites && i < func->obj_site_count; i++) { ant_shape_t *shape = func->obj_sites[i].shared_shape; func->obj_sites[i].shared_shape = NULL; if (shape) ant_shape_release(shape); }
for (uint32_t i = 0; func->ic_slots && i < func->ic_count; i++) { sv_ic_entry_t *ic = &func->ic_slots[i]; sv_gf_poly_free(js, ic); sv_pf_poly_free(js, ic); ant_shape_t **slots[3] = { (ic->shape_ref_mask & SV_IC_SHAPE_REF_CACHED) ? &ic->cached_shape : NULL, (ic->shape_ref_mask & SV_IC_SHAPE_REF_ADD_FROM) ? &ic->guard.add.from_shape : NULL, (ic->shape_ref_mask & SV_IC_SHAPE_REF_ADD_TO) ? &ic->guard.add.to_shape : NULL, }; for (int k = 0; k < 3; k++) { if (!slots[k]) continue; ant_shape_t *shape = *slots[k]; *slots[k] = SV_IC_SHAPE_SLOT_DEAD; if (shape) ant_shape_release(shape); } ic->shape_ref_mask = 0; }}
static void unit_free(ant_t *js, sv_code_unit_t *unit) { sv_code_units_t *u = &js->code_units; for (sv_func_t *func = unit->funcs; func; func = func->unit_next) { func_release(js, func); func_free_sidecar(func); } u->held_bytes -= SV_CODE_UNIT_OVERHEAD; for (size_t i = 0; i < unit->block_count; i++) { sv_code_block_t *block = unit->blocks[i]; if (--block->live_units == 0 && block != u->current) block_release(u, block); } free(unit->blocks); free(unit->compile_roots); free(unit);}
void sv_code_units_sweep(ant_t *js, uint64_t epoch) { sv_code_units_t *u = &js->code_units; sv_code_unit_t *dying = NULL;
for (sv_code_unit_t **link = &u->units; *link;) { sv_code_unit_t *unit = *link; if (!sv_code_unit_dying(unit, epoch)) { link = &unit->next; continue; } *link = unit->next; u->unit_count--; unit->next = dying; dying = unit; }
for (sv_code_unit_t *unit = dying, *next; unit; unit = next) { next = unit->next; unit_free(js, unit); }}
static void units_check_pins(ant_t *js) {#ifndef NDEBUG if (js->vm_exec_depth) return; for (sv_code_unit_t *unit = js->code_units.pinned; unit; unit = unit->pinned_next) ANT_ASSERT(unit->immortal, "code unit still pinned at teardown (missing sv_code_unit_unpin)");#endif}
void sv_code_units_destroy(ant_t *js) { sv_code_units_t *u = &js->code_units; units_check_pins(js);
for (sv_func_t *func = u->unowned_funcs; func; func = func->unit_next) { func_release(js, func); func_free_sidecar(func); } u->unowned_funcs = NULL;
for (sv_code_unit_t *unit = u->units, *next; unit; unit = next) { next = unit->next; unit_free(js, unit); } u->units = NULL; u->pinned = NULL; u->unit_count = 0;
if (u->current) ant_cage_free(u->current, u->current->alloc_size); u->current = NULL;
for (sv_code_block_t *block = u->spare, *next; block; block = next) { next = block->next; ant_cage_free(block, block->alloc_size); } u->spare = NULL; u->spare_count = 0;
free(u->young_values); u->young_values = NULL; u->young_len = u->young_cap = 0;
free(u->fb_watch); u->fb_watch = NULL; u->fb_watch_len = u->fb_watch_cap = 0;}