diff --git a/packages/compiler/src/backend/llvm/shapes.test.ts b/packages/compiler/src/backend/llvm/shapes.test.ts index 05797ebbb..8c2c46541 100644 --- a/packages/compiler/src/backend/llvm/shapes.test.ts +++ b/packages/compiler/src/backend/llvm/shapes.test.ts @@ -144,7 +144,11 @@ describe("LLVM runtime RC symbols", () => { // The dyn fast path buffers surviving values by kind, as scr_dyn_release does. const ir = emitInlineRcHelpers(host).join("\n"); expect(ir).toContain("call void @scr_dyn_release_survivor(ptr %o)"); - expect(ir).toMatch(/switch i32 %kind, label %done \[ i32 4, label %root/); + expect(ir).toMatch( + /switch i32 %kind, label %done \[ i32 4, label %container i32 5, label %container i32 8, label %root/, + ); + // Arrays and objects not yet buffered ask the runtime, which skips intact documents. + expect(ir).toMatch(/br i1 %cqueued, label %done, label %survivor/); }); test("sanitized builds call the fast-path helpers out of line", () => { diff --git a/packages/compiler/src/backend/llvm/shapes.ts b/packages/compiler/src/backend/llvm/shapes.ts index 4e8b5d819..c3d4aff8d 100644 --- a/packages/compiler/src/backend/llvm/shapes.ts +++ b/packages/compiler/src/backend/llvm/shapes.ts @@ -283,7 +283,8 @@ interface InlineRcFamily { * * The dyn release buffers a surviving value by kind (scr_dyn_release): * the fast path decides the always and never cases and calls - * scr_dyn_release_survivor for kinds whose candidacy is per value. + * scr_dyn_release_survivor for kinds whose candidacy is per value, + * including arrays and objects of intact documents (ScrDynRegion). * * Releases stay plain calls for: * - arr: array releases are the most numerous release sites and the @@ -552,12 +553,19 @@ function releaseHelper(host: ShapeHost, family: string, row: InlineRcFamily): st : family === "dyn" ? [ // scr_dyn_release buffers a surviving value by kind: always for - // arrays, objects, functions and proxies, per value for capsules - // and handles (scr_dyn_release_survivor), never for scalars. + // functions and proxies, per value for arrays and objects (not + // while their document is intact, ScrDynRegion), capsules and + // handles (scr_dyn_release_survivor), never for scalars. An + // array or object already in a candidate buffer stays inline. ` %kp = getelementptr i8, ptr %o, ${S} ${S === "i64" ? 8 : 4} ; ScrDyn.kind`, ` %kind = load i32, ptr %kp`, // SCR_DYN_ARR 4, OBJ 5, FUNC 8, PROXY 13; HANDLE 9, TYPED_REF 12. - ` switch i32 %kind, label %done [ i32 4, label %root i32 5, label %root i32 8, label %root i32 13, label %root i32 9, label %survivor i32 12, label %survivor ]`, + ` switch i32 %kind, label %done [ i32 4, label %container i32 5, label %container i32 8, label %root i32 13, label %root i32 9, label %survivor i32 12, label %survivor ]`, + `container:`, + ` %cbufp = getelementptr i8, ptr %o, ${S} -${host.cycleBufOffset}`, + ` %cbuf = load i32, ptr %cbufp`, + ` %cqueued = icmp slt i32 %cbuf, 0`, + ` br i1 %cqueued, label %done, label %survivor`, `survivor:`, ` call void @scr_dyn_release_survivor(ptr %o)`, ` br label %done`, diff --git a/packages/runtime/src/scr_json.c b/packages/runtime/src/scr_json.c index 6c5eb97ee..188246762 100644 --- a/packages/runtime/src/scr_json.c +++ b/packages/runtime/src/scr_json.c @@ -1086,10 +1086,17 @@ static void scr_dyn_gcfree(void *ptr) { scr_dyn_dispose(ptr, true); } static void scr_dyn_destroy(void *ptr) { scr_dyn_dispose(ptr, false); } -/* The cycle bookkeeping of a capsule or handle that survives a release. - * Compiled release fast paths decrement first and call this for the kinds - * whose candidacy depends on per-value state, matching scr_dyn_release. */ +/* The cycle bookkeeping of a value that survives a release. Compiled + * release fast paths decrement first and call this for the kinds whose + * candidacy depends on per-value state, matching scr_dyn_release: arrays + * and objects not yet buffered (skipped while their document is intact, + * ScrDynRegion), capsules and handles. */ void scr_dyn_release_survivor(ScrDyn *d) { + if (d->kind == SCR_DYN_ARR || d->kind == SCR_DYN_OBJ) { + const ScrDynRegion *region = scr_dyn_region_of(d); + if (!region || !region->intact) scr_cyc_released(d); + return; + } if ((d->kind == SCR_DYN_TYPED_REF && (d->v.typed_ref.materialized || d->v.typed_ref.casts || d->v.typed_ref.traced || d->prototype || d->symbol_properties || d->symbol_keys)) || @@ -3904,6 +3911,7 @@ ScrDynEntry *scr_dyn_own_symbol_entry(const ScrDyn *object, const ScrDyn *key) { ScrStr *scr_dyn_symbol_storage_name(const ScrDyn *key) { return scr_dyn_symbol_storage_key(key); } void scr_dyn_symbol_init_own(ScrDyn *object, ScrDyn *key, const ScrStr *storage, ScrDyn *value) { + scr_dyn_region_store(object, NULL); /* intact containers have no symbol tables */ object->symbol_properties = scr_dyn_new_obj_null_proto(); scr_dyn_obj_set(object->symbol_properties, storage->data, storage->len, scr_dyn_retain(value)); object->symbol_keys = scr_dyn_new_arr(); diff --git a/packages/runtime/src/scr_symbol.c b/packages/runtime/src/scr_symbol.c index e3baf8bc8..b3d60d354 100644 --- a/packages/runtime/src/scr_symbol.c +++ b/packages/runtime/src/scr_symbol.c @@ -342,6 +342,7 @@ static void scr_dirent_init(void) { void scr_dyn_dirent_finish(ScrDyn *obj) { scr_dirent_init(); + scr_dyn_region_store(obj, NULL); /* a prototype store (ScrDynRegion) */ scr_dyn_release(obj->prototype); obj->prototype = scr_dyn_retain(scr_dirent_prototype); for (size_t i = 0; i < obj->v.obj.len; i++) {