diff --git a/include/ant.h b/include/ant.h index 74414b6..48b8721 100644 --- a/include/ant.h +++ b/include/ant.h @@ -189,6 +189,7 @@ void js_set_keys(ant_value_t obj, js_keys_fn keys); void js_set_finalizer(ant_value_t obj, js_finalizer_fn fn); ant_value_t js_get_slot(ant_value_t obj, internal_slot_t slot); +ant_value_t js_promise_assimilate_awaitable(ant_t *js, ant_value_t value); ant_value_t js_promise_then(ant_t *js, ant_value_t promise, ant_value_t on_fulfilled, ant_value_t on_rejected); void js_set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t value); diff --git a/include/gc.h b/include/gc.h index 0130c71..30e4d21 100644 --- a/include/gc.h +++ b/include/gc.h @@ -10,8 +10,6 @@ #define GC_FORCE_INTERVAL_MS 50 #define GC_MAJOR_EVERY_N_MINOR 8 -#define GC_HEAP_GROWTH(n) ((n) + (n) / 2) - #define GC_OBJ_TYPE_MASK (JS_TPFLG(T_OBJ) \ | JS_TPFLG(T_ARR) \ | JS_TPFLG(T_PROMISE) \ @@ -31,6 +29,9 @@ void gc_run_minor(ant_t *js); void gc_maybe(ant_t *js); void gc_remember_add(ant_t *js, ant_object_t *obj); +size_t gc_live_major_threshold(ant_t *js); +size_t gc_pool_major_threshold(ant_t *js); + void gc_func_mark_profile_enable(bool enabled); void gc_func_mark_profile_reset(void); diff --git a/src/ant.c b/src/ant.c index f849243..89c79bf 100644 --- a/src/ant.c +++ b/src/ant.c @@ -394,9 +394,7 @@ static ant_exotic_ops_t *obj_ensure_exotic_ops(ant_object_t *obj) { } static ant_object_t *obj_alloc(ant_t *js, uint8_t type_tag, uint8_t inobj_limit) { - size_t threshold = GC_HEAP_GROWTH(js->gc_last_live); - - if (threshold < 2048) threshold = 2048; + size_t threshold = gc_live_major_threshold(js); if (js->obj_arena.live_count >= threshold) gc_run(js); ant_object_t *obj = (ant_object_t *)fixed_arena_alloc(&js->obj_arena); @@ -1859,12 +1857,32 @@ static size_t fix_exponent(char *buf, size_t n) { return n; } +static size_t strnum_safe_integer(double dv, char *buf, size_t len) { + char temp[32]; + size_t pos = sizeof(temp); + uint64_t value = 0; + bool negative = signbit(dv) != 0; + + if (negative) dv = -dv; + value = (uint64_t)dv; + + do { + temp[--pos] = (char)('0' + (value % 10u)); + value /= 10u; + } while (value != 0); + + if (negative) temp[--pos] = '-'; + return cpy(buf, len, temp + pos, sizeof(temp) - pos); +} + static size_t strnum(ant_value_t value, char *buf, size_t len) { double dv = tod(value); - - if (isnan(dv)) return cpy(buf, len, "NaN", 3); - if (isinf(dv)) return cpy(buf, len, dv > 0 ? "Infinity" : "-Infinity", dv > 0 ? 8 : 9); if (dv == 0.0) return cpy(buf, len, "0", 1); + + if (__builtin_expect(isnan(dv), 0)) + return cpy(buf, len, "NaN", 3); + if (__builtin_expect(isinf(dv), 0)) + return cpy(buf, len, dv > 0 ? "Infinity" : "-Infinity", dv > 0 ? 8 : 9); char temp[64]; int sign = dv < 0 ? 1 : 0; @@ -1873,9 +1891,7 @@ static size_t strnum(ant_value_t value, char *buf, size_t len) { double iv; double frac = modf(adv, &iv); if (frac == 0.0 && adv < 9007199254740992.0) { - int result = snprintf(temp, sizeof(temp), "%.0f", dv); - fix_exponent(temp, (size_t)result); - return cpy(buf, len, temp, strlen(temp)); + return strnum_safe_integer(dv, buf, len); } for (int prec = 1; prec <= 17; prec++) { @@ -1885,7 +1901,6 @@ static size_t strnum(ant_value_t value, char *buf, size_t len) { fix_exponent(temp, (size_t)n); return cpy(buf, len, temp, strlen(temp)); } - (void)n; } int result = snprintf(temp, sizeof(temp), "%.17g", dv); @@ -8053,9 +8068,8 @@ static ant_value_t builtin_array_join(ant_t *js, ant_value_t *args, int nargs) { elem_str = (const char *)(uintptr_t)(soff); elem_len = slen; } else if (et == T_NUM) { - snprintf(numstr, sizeof(numstr), "%g", tod(elem)); + elem_len = strnum(elem, numstr, sizeof(numstr)); elem_str = numstr; - elem_len = strlen(numstr); } else if (et == T_BOOL) { elem_str = vdata(elem) ? "true" : "false"; elem_len = strlen(elem_str); @@ -11286,6 +11300,27 @@ static inline ant_value_t js_get_thenable_then(ant_t *js, ant_value_t value) { return js_getprop_fallback(js, value, "then"); } +ant_value_t js_promise_assimilate_awaitable(ant_t *js, ant_value_t value) { + if (vtype(value) == T_PROMISE || !is_object_type(value)) return value; + + GC_ROOT_SAVE(root_mark, js); + GC_ROOT_PIN(js, value); + + ant_value_t then_prop = js_get_thenable_then(js, value); + GC_ROOT_PIN(js, then_prop); + + if (vtype(then_prop) == T_FUNC || vtype(then_prop) == T_CFUNC) { + ant_value_t promise = js_mkpromise(js); + GC_ROOT_PIN(js, promise); + js_resolve_promise(js, promise, value); + GC_ROOT_RESTORE(js, root_mark); + return promise; + } + + GC_ROOT_RESTORE(js, root_mark); + return value; +} + js_await_result_t js_promise_await_coroutine(ant_t *js, ant_value_t promise, coroutine_t *coro) { js_await_result_t result = { .state = JS_AWAIT_PENDING, diff --git a/src/gc/gc.c b/src/gc/gc.c index 63fd64e..8116297 100644 --- a/src/gc/gc.c +++ b/src/gc/gc.c @@ -16,6 +16,9 @@ static uint64_t gc_last_run_ms = 0; static size_t gc_nursery_threshold = GC_NURSERY_THRESHOLD; static uint32_t gc_major_every_n = GC_MAJOR_EVERY_N_MINOR; +static uint32_t gc_major_live_growth_x256 = 384; +static uint32_t gc_major_pool_growth_x256 = 384; + static uint32_t gc_minor_surv_ewma = 128; static uint32_t gc_major_recl_ewma = 26; @@ -25,6 +28,22 @@ static uint64_t gc_now_ms(void) { return (uint64_t)ts.tv_sec * 1000ULL + (uint64_t)ts.tv_nsec / 1000000ULL; } +static size_t gc_scaled_threshold(size_t base_live, uint32_t growth_x256, size_t floor) { + size_t scaled = (base_live * (size_t)growth_x256) / 256u; + if (scaled < floor) scaled = floor; + return scaled; +} + +size_t gc_live_major_threshold(ant_t *js) { + size_t base_live = js ? js->gc_last_live : 0; + return gc_scaled_threshold(base_live, gc_major_live_growth_x256, 2048u); +} + +size_t gc_pool_major_threshold(ant_t *js) { + size_t base_live = js ? js->gc_pool_last_live : 0; + return gc_scaled_threshold(base_live, gc_major_pool_growth_x256, 4u * 1024u * 1024u); +} + static void gc_adapt_nursery(size_t young_before, size_t survivors) { if (young_before == 0) return; uint32_t rate = (uint32_t)((survivors * 256) / young_before); @@ -50,6 +69,22 @@ static void gc_adapt_major_interval(size_t live_before, size_t live_after) { } else if (!gen_ineffective && low_reclaim) { if (gc_major_every_n < GC_MAJOR_EVERY_N_MINOR * 4) gc_major_every_n++; } + + if (gen_ineffective && low_reclaim) { + if (gc_major_live_growth_x256 < 1024) gc_major_live_growth_x256 += 96; + if (gc_major_pool_growth_x256 < 1536) gc_major_pool_growth_x256 += 128; + } else if (low_reclaim) { + if (gc_major_live_growth_x256 < 896) gc_major_live_growth_x256 += 48; + if (gc_major_pool_growth_x256 < 1280) gc_major_pool_growth_x256 += 64; + } else if (high_reclaim) { + if (gc_major_live_growth_x256 > 320) gc_major_live_growth_x256 -= 32; + if (gc_major_pool_growth_x256 > 320) gc_major_pool_growth_x256 -= 32; + } else { + if (gc_major_live_growth_x256 > 384) gc_major_live_growth_x256 -= 16; + else if (gc_major_live_growth_x256 < 384) gc_major_live_growth_x256 += 16; + if (gc_major_pool_growth_x256 > 384) gc_major_pool_growth_x256 -= 16; + else if (gc_major_pool_growth_x256 < 384) gc_major_pool_growth_x256 += 16; + } } static void gc_mark_str(ant_t *js, ant_value_t v) { @@ -153,16 +188,17 @@ void gc_maybe(ant_t *js) { gc_tick = 0; gc_run_minor(js); if (js->minor_gc_count >= gc_major_every_n) { - js->minor_gc_count = 0; gc_run(js); + js->minor_gc_count = 0; + gc_run(js); } return; } - size_t threshold = GC_HEAP_GROWTH(js->gc_last_live); - if (threshold < 2048) threshold = 2048; + size_t threshold = gc_live_major_threshold(js); if (live >= threshold) { - gc_tick = 0; gc_run(js); + gc_tick = 0; + gc_run(js); return; } diff --git a/src/gc/objects.c b/src/gc/objects.c index fe820f2..19b3d0b 100644 --- a/src/gc/objects.c +++ b/src/gc/objects.c @@ -320,6 +320,7 @@ static void gc_scan_vm_stack(ant_t *js, sv_vm_t *vm) { gc_mark_value(js, frame->super_val); gc_mark_value(js, frame->with_obj); gc_mark_value(js, frame->completion.value); + gc_mark_value(js, frame->arguments_obj); } for (sv_upvalue_t *uv = vm->open_upvalues; uv; uv = uv->next) { diff --git a/src/pool.c b/src/pool.c index d38e2d9..25d6e76 100644 --- a/src/pool.c +++ b/src/pool.c @@ -307,9 +307,7 @@ void *js_type_alloc(ant_t *js, ant_alloc_kind_t kind, size_t size, size_t align) if (align == 0) align = sizeof(void *); js->gc_pool_alloc += size; - size_t pool_threshold = GC_HEAP_GROWTH(js->gc_pool_last_live); - - if (pool_threshold < (4u * 1024u * 1024u)) pool_threshold = 4u * 1024u * 1024u; + size_t pool_threshold = gc_pool_major_threshold(js); if (js->gc_pool_alloc >= pool_threshold) gc_run(js); if (kind == ANT_ALLOC_STRING) return string_pool_alloc(js, size, align); diff --git a/src/silver/ops/async.h b/src/silver/ops/async.h index 44082e8..ac863fa 100644 --- a/src/silver/ops/async.h +++ b/src/silver/ops/async.h @@ -66,6 +66,7 @@ static void sv_mco_async_entry(mco_coro *mco) { ); ant_value_t promise = ctx->coro->async_promise; + if (vm && vm->suspended) return; if (is_err(result)) { ant_value_t reject_value = js->thrown_exists ? js->thrown_value : result; @@ -95,6 +96,7 @@ static void sv_mco_tla_entry(mco_coro *mco) { ant_value_t result = sv_execute_entry(vm, ctx->func, ctx->this_val, NULL, 0); ant_value_t promise = ctx->coro->async_promise; + if (vm && vm->suspended) return; if (is_err(result)) { ant_value_t reject_value = js->thrown_exists ? js->thrown_value : result; @@ -501,11 +503,12 @@ static inline ant_value_t sv_start_async_closure( } static inline ant_value_t sv_await_value(ant_t *js, ant_value_t value) { + value = js_promise_assimilate_awaitable(js, value); + if (is_err(value)) return value; if (vtype(value) != T_PROMISE) return value; mco_coro *current_mco = mco_running(); - if (!current_mco) - current_mco = NULL; + if (!current_mco) current_mco = NULL; coroutine_t *coro = NULL; if (current_mco) {