diff --git a/runtime/src/keytree.c b/runtime/src/keytree.c index 5d63eecf..27837b6f 100644 --- a/runtime/src/keytree.c +++ b/runtime/src/keytree.c @@ -20,6 +20,18 @@ char *kvlangKeytreeStack(const char *root) { return r; } +/* 栈缓冲版:把 root(去尾 /)拷进 buf 并补 '/',返回写入长度(含尾 /);cap 不足返回 0。 + * 读参热路径复用调用方缓冲,免每操作一次 malloc。 */ +size_t kvlangKeytreeStackBuf(const char *root, char *buf, size_t cap) { + size_t n = strlen(root); + while (n > 0 && root[n - 1] == '/') n--; + if (n + 2 > cap) return 0; + memcpy(buf, root, n); + buf[n] = '/'; + buf[n + 1] = 0; + return n + 1; +} + char *kvlangKeytreeFrameRoot(const char *pc) { const char *last = NULL; for (const char *p = pc; (p = strstr(p, "/[")) != NULL; p += 2) last = p; @@ -30,6 +42,13 @@ char *kvlangKeytreeFrameRoot(const char *pc) { return r; } +/* 帧根长度(末个 "/[" 之前),无分配:循环每步据之判断帧是否变化,免一次 malloc。 */ +size_t kvlangKeytreeFrameRootLen(const char *pc) { + const char *last = NULL; + for (const char *p = pc; (p = strstr(p, "/[")) != NULL; p += 2) last = p; + return last ? (size_t)(last - pc) : 0; +} + static char *trim_right_join(const char *root, const char *suffix) { size_t n = strlen(root); while (n > 0 && root[n - 1] == '/') n--; @@ -59,14 +78,15 @@ char *kvlangKeytreeFrameAt(const char *vtid, int depth) { } int kvlangKeytreeFrameNum(const char *path) { - kvlangStrbuf_t vtid_b; kvlangStrbufInit(&vtid_b); - const char *vtid = kvlangKeytreeVtidFromPc(path, &vtid_b); - if (!vtid[0]) keytree_die("FrameNum: not a vthread path: %s", path); - kvlangStrbuf_t pfx; kvlangStrbufInit(&pfx); - kvlangKeytreeVthread(vtid, &pfx); - kvlangStrbufPutc(&pfx, '/'); - if (strncmp(path, pfx.p, pfx.len) != 0) keytree_die("FrameNum: not a vthread path: %s", path); - const char *rest = path + pfx.len; + /* 免分配:/vthread//[d][/...] —— 直接定位 vtid 后的帧坐标,不构造前缀串。 */ + const size_t rl = sizeof(VTHREAD_ROOT) - 1; + if (strncmp(path, VTHREAD_ROOT PATH_SEP, rl + 1) != 0) + keytree_die("FrameNum: not a vthread path: %s", path); + const char *vtid = path + rl + 1; + const char *slash = strchr(vtid, '/'); + if (!slash || slash == vtid) + keytree_die("FrameNum: not a vthread path: %s", path); + const char *rest = slash + 1; if (rest[0] != '[') keytree_die("FrameNum: no frame coord in %s", path); const char *end = strchr(rest, ']'); if (!end) keytree_die("FrameNum: unterminated frame coord in %s", path); @@ -90,7 +110,6 @@ int kvlangKeytreeFrameNum(const char *path) { } else { keytree_die("FrameNum: expected /[irseq,j] after [d] in %s", path); } - kvlangStrbufFree(&vtid_b); kvlangStrbufFree(&pfx); return (int)n; } diff --git a/runtime/src/kvcpu.c b/runtime/src/kvcpu.c index 7efa1ca5..b7e3426b 100644 --- a/runtime/src/kvcpu.c +++ b/runtime/src/kvcpu.c @@ -121,8 +121,7 @@ static int irseq_of(const kvlangParam_t *p) { } /* 函数内跳转:只改 PC 的 [irseq],帧不变。目标非法 → RuntimeError,返回 -1。 */ -static int jump_to(kvlangKv_t *kv, const char *vtid, const char *pc, - const kvlangParam_t *target, const char *op) { +static int jump_to(kvlangFrame_t *f, const kvlangParam_t *target, const char *op) { int irseq = irseq_of(target); if (irseq < 0) { char msg[256]; @@ -130,14 +129,17 @@ static int jump_to(kvlangKv_t *kv, const char *vtid, const char *pc, "RuntimeError: %s target is not an int64 irseq: %s (kind=%s)", op, target->name ? target->name : "", kvlangXvalueKind(&target->val)); - kvlangVthreadSetError(kv, vtid, pc, msg); + kvlangVthreadSetError(f->kv, f->vtid, f->pc, msg); return -1; } - char *fr = kvlangKeytreeFrameRoot(pc); + /* 帧根优先用主循环缓存的(br/goto 帧不变,借它免每次 malloc)。 */ + char *owned = NULL; + const char *fr = f->frame_root; + if (!fr) { owned = kvlangKeytreeFrameRoot(f->pc); fr = owned; } char *np = kvlangKeytreeIrseqPc(fr, irseq); - free(fr); - kvlangVthreadSet(kv, vtid, np, "running"); - kvlangLogDebug("[%s] %s → %s", vtid, op, np); + free(owned); + kvlangVthreadAdvance(f, np, "running"); + kvlangLogDebug("[%s] %s → %s", f->vtid, op, np); free(np); return 0; } @@ -789,7 +791,7 @@ int kvlangCtlCall(kvlangFrame_t *f) { char *sub = handle_call(f->kv, f->pc, f->inst); if (!sub) return -1; - kvlangVthreadSet(f->kv, f->vtid, sub, "running"); + kvlangVthreadAdvance(f, sub, "running"); free(sub); return 0; } @@ -802,7 +804,7 @@ int kvlangCtlReturn(kvlangFrame_t *f) { kvlangVthreadSetDone(f->kv, f->vtid, "ok"); return 0; } - kvlangVthreadSet(f->kv, f->vtid, parent, "running"); + kvlangVthreadAdvance(f, parent, "running"); free(parent); return 0; } @@ -816,7 +818,7 @@ int kvlangCtlGoto(kvlangFrame_t *f) { kvlangVthreadSetError(f->kv, f->vtid, f->pc, msg); return -1; } - return jump_to(f->kv, f->vtid, f->pc, &inst->reads[0], OP_GOTO); + return jump_to(f, &inst->reads[0], OP_GOTO); } int kvlangCtlBr(kvlangFrame_t *f) { @@ -852,7 +854,7 @@ int kvlangCtlBr(kvlangFrame_t *f) { } bool taken = kvlangScalarI64(kvlangXvalueScalar(&cond)) != 0; kvlangXvalueFree(&cond); - return jump_to(f->kv, f->vtid, f->pc, &inst->reads[taken ? 1 : 2], OP_BR); + return jump_to(f, &inst->reads[taken ? 1 : 2], OP_BR); } /* 动态调用:以运行时得到的 funckey 在当前 vthread 造一次 OP_CALL(不新开 vid), @@ -1027,28 +1029,40 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, char *cur = strdup(pc); char *cur_frame = NULL, *cur_funcdir = NULL; /* 帧不变时 funcdir 只读一次,供缓存键 */ + int cur_depth = 0; /* 帧深度,随 cur_frame 一起缓存 */ int rc = 0; - /* status 跨轮携带:尾部 VthreadGet 已连 pc 一并取出,下轮直接复用,省掉背靠背重读 - * (vthread 记录在两次 get 之间不被改写;status 串由 ValueString 自持,跨 ReadReset 存活)。 */ + /* status:每步从 kvspace 回读的**源值**,不当缓存用(下轮开头即重读)。 + * 同时借给本步的 kvlangFrame_t.status_known,供 Advance 判断是否真要写 status。 */ char *status = NULL; - { - char *pcv = NULL; - kvlangVthreadGet(kv, vtid, &pcv, &status); - free(pcv); - } for (;;) { /* 指令边界:回收上条指令执行期借出的读池(cache 指令的读参已 Materialize 自持,不受影响)。 * durable 惰性写不再清池,全靠此处回收;shm 常驻映射侧为 no-op。 */ kvlangKvReadReset(kv); + /* 状态门(spec [[执行模型]] 步骤 1):每指令边界从 kvspace 回读 ‥status,只读 1 个 key + * (走 vtid key 缓存)。不得改用进程内副本——那是状态的第二份事实源:外部把 ‥status + * 置 paused/error(暂停、接管、终止)必须在下一步就被看见,且 kvspace 始终等于真实状态。 */ + free(status); + status = NULL; + kvlangVthreadStatusGet(kv, vtid, &status); if (!status || (strcmp(status, "init") != 0 && strcmp(status, "running") != 0 && strcmp(status, "wait") != 0)) { break; } - free(status); - status = NULL; - int depth = kvlangKeytreeFrameNum(cur); + /* 帧变(call/return/goto 跨帧)才重算根/深度/funcdir;帧内每步只比较前缀,免分配。 */ + size_t frlen = kvlangKeytreeFrameRootLen(cur); + if (!cur_frame || strlen(cur_frame) != frlen || + strncmp(cur_frame, cur, frlen) != 0) { + free(cur_frame); + cur_frame = malloc(frlen + 1); + memcpy(cur_frame, cur, frlen); + cur_frame[frlen] = 0; + free(cur_funcdir); + cur_funcdir = read_seglib(kv, cur_frame); + cur_depth = kvlangKeytreeFrameNum(cur); + } + int depth = cur_depth; if (depth > MAX_STACK_DEPTH) { char msg[256]; snprintf(msg, sizeof msg, @@ -1059,13 +1073,7 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, break; } - char *fr = kvlangKeytreeFrameRoot(cur); - if (!cur_frame || strcmp(cur_frame, fr) != 0) { - free(cur_frame); - cur_frame = strdup(fr); - free(cur_funcdir); - cur_funcdir = read_seglib(kv, fr); - } + const char *fr = cur_frame; /* 借用(随 cur_frame 缓存同步),循环内无需 free */ const char *lastc = NULL; for (const char *p = cur; (p = strstr(p, "/[")) != NULL; p += 2) lastc = p; @@ -1086,7 +1094,6 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, snprintf(msg, sizeof msg, "decode: %s", err); kvlangVthreadSetError(kv, vtid, cur, msg); free(link_base); - free(fr); rc = -1; break; } @@ -1114,7 +1121,6 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, snprintf(msg, sizeof msg, "RuntimeError: no instruction at %s", cur); kvlangVthreadSetError(kv, vtid, cur, msg); - free(fr); if (tmp_owned) kvlangRwirInstFree(&tmp); rc = -1; @@ -1123,9 +1129,12 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, int exec_err = 0; char *yield = NULL; + char *fb_pc = NULL; /* 本指令的 PC 回传槽(见 kvlangFrame_t),免写后回读 */ if (inst->op_id >= 0) { /* 单表派发:native 算子与 control/copy 同居 myrwircaps,op_id 直查一跳到底。 */ - kvlangFrame_t f = {kv, vtid, cur, inst, &yield}; + kvlangFrame_t f = {kv, vtid, cur, inst, &yield, &fb_pc}; + f.frame_root = cur_frame; /* 复用循环缓存的帧根,免每步重算 */ + f.status_known = status; /* 本步开始前读到的 ‥status(源值,借用) */ exec_err = kvlangBuiltinNative(&f); if (exec_err == 0 && yield) { /* native(vthread·run return 模式)冒泡一个子 vthread 的 rwir pc 给上层驱动。 @@ -1134,9 +1143,9 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, *out_pc = yield; else free(yield); - free(fr); if (tmp_owned) kvlangRwirInstFree(&tmp); + free(fb_pc); free(cur); free(cur_frame); free(cur_funcdir); @@ -1153,9 +1162,9 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, if (exec_err == 0 && mode == KVMODE_RETURN) { if (out_pc) *out_pc = strdup(cur); - free(fr); if (tmp_owned) kvlangRwirInstFree(&tmp); + free(fb_pc); free(cur); free(cur_frame); free(cur_funcdir); @@ -1181,6 +1190,7 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, } ci.writes = inst->writes; kvlangFrame_t cf = {kv, vtid, cur, &ci, NULL}; + cf.status_known = status; exec_err = kvlangCtlCall(&cf); free(ci.opcode); free(ci.reads[0].name); @@ -1188,17 +1198,23 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, } if (exec_err != 0) { - free(fr); if (tmp_owned) kvlangRwirInstFree(&tmp); + free(fb_pc); rc = -1; break; } char *newpc = NULL; - kvlangVthreadGet(kv, vtid, &newpc, - &status); /* status 连 pc 一并取出,供下轮直接复用 */ - free(fr); + if (fb_pc) { + /* 本指令经帧回传了新 PC:直接用(它就是本指令刚写进 kvspace 的值)。 + * status 不回传——下一轮状态门照旧从 kvspace 回读。 */ + newpc = fb_pc; + fb_pc = NULL; + } else { + /* 无回传(ext rwir / 用户 call / done 等):从 kvspace 回读 PC。 */ + kvlangVthreadPcGet(kv, vtid, &newpc); + } if (tmp_owned) kvlangRwirInstFree(&tmp); if (!newpc || !newpc[0]) { diff --git a/runtime/src/kvspace.c b/runtime/src/kvspace.c index de2df63e..cff38e63 100644 --- a/runtime/src/kvspace.c +++ b/runtime/src/kvspace.c @@ -45,7 +45,11 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv if (!name || !name[0]) return 0; size_t dl = strlen(dir), nl = strlen(name); - char *key = malloc(dl + nl + 1); + /* 短 key 走栈缓冲,免读参热路径每步一次 malloc。 */ + char stackbuf[512]; + char *key = stackbuf; + if (dl + nl + 1 > sizeof stackbuf) + key = malloc(dl + nl + 1); memcpy(key, dir, dl); memcpy(key + dl, name, nl); key[dl + nl] = 0; @@ -56,7 +60,8 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv out->len = len; out->borrowed = 1; } - free(key); + if (key != stackbuf) + free(key); return 0; } @@ -123,6 +128,24 @@ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, ui return rc; } +/* 直写 char/utf8 值:跳过「TLV 编码 → KvSet 再解 head」的往返,直接向后端要 body 指针填字节。 + * 供 PC 推进热路径(每指令一次)用;语义等价于 NewCharUtf8 + KvSet。 */ +int kvlangKvSetChar(kvlangKv_t *k, const char *key, const char *s) { + uint32_t len = (uint32_t)strlen(s); + uint8_t *dst = NULL; + char err[256]; + if (kvspaceWriteInPlace(k->h, key, 0, len, &dst, err, sizeof err) != 0) { + if (kvspaceWriteNewPlace(k->h, key, KVSPACE_REF_INLINE, + KVSPACE_STORETYPE_ATOM, 0, 0, + KVSPACE_KIND_CHAR_UTF8, len, &dst, err, + sizeof err) != 0) + return -1; + } + if (len && dst) + memcpy(dst, s, len); + return 0; +} + int kvlangKvDel(kvlangKv_t *k, const char *key, char *err, uint32_t err_cap) { const char *keys[1] = {key}; return kvspaceDel(k->h, keys, 1, err, err_cap); diff --git a/runtime/src/myrwircaps.c b/runtime/src/myrwircaps.c index f17bb381..8827c133 100644 --- a/runtime/src/myrwircaps.c +++ b/runtime/src/myrwircaps.c @@ -96,7 +96,7 @@ int kvlangDefRwir(void *kvspace, const char *opcode, const char *const *rp, /* 精度前缀(int64·add / float32·add …)保留:CapIndex 两级查表——先按完整 opcode 命中特化, * 未命中且前缀是 C native 数字 kind 时才剥前缀归到裸 op(如 add),kvlangBuiltin* 按操作数 kind 归约。 */ -static int kvlangCtlCopy(kvlangFrame_t *f) { return kvlangBuiltinExecuteCopy(f->kv, f->vtid, f->pc, f->inst); } +static int kvlangCtlCopy(kvlangFrame_t *f) { return kvlangBuiltinExecuteCopy(f); } static const struct { const char *op; kvlangBuiltinFn fn; } myrwircaps[] = { /* control / copy:与 native 算子同表,op_id 单跳派发 */ diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index aafe0cd8..7c5aa288 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -336,6 +336,7 @@ int kvlangKvGetHead(kvlangKv_t *k, const char *key, kvspaceHead_t *out); /* 只读 head,不取 body */ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, uint32_t err_cap); +int kvlangKvSetChar(kvlangKv_t *k, const char *key, const char *s); int kvlangKvDel(kvlangKv_t *k, const char *key, char *err, uint32_t err_cap); int kvlangKvDelTree(kvlangKv_t *k, const char *prefix, char *err, uint32_t err_cap); @@ -378,7 +379,9 @@ static inline void kvlangStrbufClear(kvlangStrbuf_t *b) { const char *kvlangKeytreeVtidFromPc(const char *pc, kvlangStrbuf_t *out); /* "" 无效 */ char *kvlangKeytreeStack(const char *root); /* malloc */ +size_t kvlangKeytreeStackBuf(const char *root, char *buf, size_t cap); /* 栈缓冲,返长度 */ char *kvlangKeytreeFrameRoot(const char *pc); /* malloc,无效 NULL */ +size_t kvlangKeytreeFrameRootLen(const char *pc); /* 帧根长度,无分配 */ char *kvlangKeytreeEntryPc(const char *root); /* malloc */ char *kvlangKeytreeFrameAt(const char *vtid, int depth); /* malloc */ int kvlangKeytreeFrameNum(const char *path); /* [d]; panics if invalid */ @@ -439,6 +442,7 @@ typedef struct { } kvlangRwirInst_t; int kvlangRwirNextPc(const char *pc, kvlangStrbuf_t *out); +size_t kvlangRwirNextPcBuf(const char *pc, char *buf, size_t cap); /* 栈缓冲,返长度 */ int kvlangRwirExtractAddr0(const char *coord); int kvlangRwirDecode(kvlangKv_t *kv, const char *link_base, const char *pc, kvlangRwirInst_t *out, char *err, uint32_t err_cap); @@ -455,6 +459,8 @@ bool notinmyrwircaps(kvlangKv_t *kv, const char *opcode); void kvlangVthreadGet(kvlangKv_t *kv, const char *vtid, char **pc, char **status); +void kvlangVthreadPcGet(kvlangKv_t *kv, const char *vtid, char **pc); +void kvlangVthreadStatusGet(kvlangKv_t *kv, const char *vtid, char **status); void kvlangVthreadSet(kvlangKv_t *kv, const char *vtid, const char *pc, const char *status); void kvlangVthreadSetDone(kvlangKv_t *kv, const char *vtid, const char *ret); @@ -464,21 +470,31 @@ void kvlangVthreadSetError(kvlangKv_t *kv, const char *vtid, const char *pc, /* ── builtin ───────────────────────────────────────────────────────── */ /* yield_pc:native builtin 把「须交回上层驱动就地派发的 pc」写入 *yield_pc(否则留 NULL)。 - * 唯 vthread·run 的 return 模式用:驱动一个子 vthread 遇非本执行器 rwir 时,把其 pc 冒泡给驱动。 */ + * 唯 vthread·run 的 return 模式用:驱动一个子 vthread 遇非本执行器 rwir 时,把其 pc 冒泡给驱动。 + * fb_pc:主循环给的 **PC 回传槽**(非 NULL 时)。新 PC 照旧先写 kvspace(崩溃恢复),同时回传此槽, + * 令主循环直接用之、免掉「刚写就回读」的那次后端 Get + 路径重建;回传值就是本指令自己刚写进去的 + * 值,不引入第二份事实源。**status 不回传**——状态门每步从 kvspace 回读(见 kvlangVthreadAdvance)。 + * fb_pc 由 Advance malloc 写入、循环接管所有权。 */ typedef struct { kvlangKv_t *kv; const char *vtid; const char *pc; kvlangRwirInst_t *inst; char **yield_pc; + char **fb_pc; + const char *frame_root; /* 主循环已缓存的帧根(借用);NULL 时各 helper 自行计算 */ + const char *status_known; /* 本步开始前从 kvspace 读到的 ‥status(借用);NULL = 未知 */ } kvlangFrame_t; +/* 循环内 PC/status 推进:PC 恒写 kvspace;status 与 f->status_known(来自 kvspace 的**源值**) + * 不同才写——绝不拿进程内副本当依据,故不会与 kvspace 分叉;fb_pc 非 NULL 时回传新 PC。 */ +void kvlangVthreadAdvance(kvlangFrame_t *f, const char *pc, const char *status); + /* notinmycaps:查 myrwircaps table,opcode 不在本 runtime 能力表内 → true。 */ bool notinmycaps(const char *opcode); bool kvlangBuiltinNumOp(const char *opcode); int kvlangBuiltinNative(kvlangFrame_t *f); /* dispatch + call,0 成功 */ -int kvlangBuiltinExecuteCopy(kvlangKv_t *kv, const char *vtid, const char *pc, - kvlangRwirInst_t *inst); +int kvlangBuiltinExecuteCopy(kvlangFrame_t *f); /* control 算子:与 native 同居 myrwircaps 一张表,frame 签名统一派发(call/return/goto/br)。 */ int kvlangCtlCall(kvlangFrame_t *f); int kvlangCtlReturn(kvlangFrame_t *f); diff --git a/runtime/src/rwir_decode.c b/runtime/src/rwir_decode.c index 88dfb763..1fc89212 100644 --- a/runtime/src/rwir_decode.c +++ b/runtime/src/rwir_decode.c @@ -19,6 +19,20 @@ int kvlangRwirNextPc(const char *pc, kvlangStrbuf_t *out) { return 0; } +/* 免分配版:把 pc 末段 [n,0] 换成 [n+1,0] 写入 buf,返回长度;cap 不足返回 0。 + * 供每指令的 PC 推进热路径复用调用方缓冲,免 strbuf 的 realloc + Printf。 */ +size_t kvlangRwirNextPcBuf(const char *pc, char *buf, size_t cap) { + const char *slash = strrchr(pc, '/'); + size_t plen = slash ? (size_t)(slash - pc) + 1 : 0; + int num = kvlangRwirExtractAddr0(slash ? slash + 1 : pc); + char tail[32]; + int tl = snprintf(tail, sizeof tail, "[%d,0]", num + 1); + if (plen + (size_t)tl + 1 > cap) return 0; + memcpy(buf, pc, plen); + memcpy(buf + plen, tail, (size_t)tl + 1); + return plen + (size_t)tl; +} + void kvlangRwirInstFree(kvlangRwirInst_t *inst) { free(inst->opcode); for (int i = 0; i < inst->nr; i++) { diff --git a/runtime/src/rwir_func.c b/runtime/src/rwir_func.c index b4dfe25f..82b5a7b6 100644 --- a/runtime/src/rwir_func.c +++ b/runtime/src/rwir_func.c @@ -19,7 +19,10 @@ void kvlangBuiltinResolveReadValue(kvlangKv_t *kv, const char *frame_root, const /* 显式解引用:*[0,±k] → 读 frame_root/[0,±k](ref=1 Ptr)→ 解引用到实参值。 * 同 ResolveWriteSlot:帧槽必有 Ptr,读不到即传参链路已坏 → panic, * 绝不静默留 None(那会让形参读成空值,把真因藏到几层之外)。 */ - char *stk = kvlangKeytreeStack(frame_root); + char stkbuf[512]; + char *stk = kvlangKeytreeStackBuf(frame_root, stkbuf, sizeof stkbuf) + ? stkbuf + : kvlangKeytreeStack(frame_root); kvlangXvalue_t pv; kvlangXvalueZero(&pv); kvlangKvGetMember(kv, stk, name + 1, &pv); if (!kvlangXvalueIsPtr(&pv)) { @@ -29,10 +32,15 @@ void kvlangBuiltinResolveReadValue(kvlangKv_t *kv, const char *frame_root, const char *target = kvlangXvaluePtrTarget(&pv); kvlangKvGetOne(kv, target, out); free(target); - kvlangXvalueFree(&pv); free(stk); + kvlangXvalueFree(&pv); + if (stk != stkbuf) + free(stk); return; } - char *stk = kvlangKeytreeStack(frame_root); + char stkbuf[512]; + char *stk = kvlangKeytreeStackBuf(frame_root, stkbuf, sizeof stkbuf) + ? stkbuf + : kvlangKeytreeStack(frame_root); kvlangXvalue_t pv; kvlangXvalueZero(&pv); kvlangKvGetMember(kv, stk, name, &pv); if (kvlangXvalueIsPtr(&pv)) { @@ -41,7 +49,9 @@ void kvlangBuiltinResolveReadValue(kvlangKv_t *kv, const char *frame_root, const } else if (!kvlangXvalueNone(&pv)) { *out = pv; pv.data = NULL; pv.len = 0; } - kvlangXvalueFree(&pv); free(stk); + kvlangXvalueFree(&pv); + if (stk != stkbuf) + free(stk); } /* 读参 → 其最终存储键(malloc):字面量(指令内冻结的具体值,无后端槽)返 NULL; @@ -57,7 +67,10 @@ char *kvlangBuiltinResolveReadKey(kvlangKv_t *kv, const char *frame_root, const char *kvlangBuiltinResolveWriteSlot(kvlangKv_t *kv, const char *frame_root, const char *name) { if (name[0] == '/') return strdup(name); - char *stk = kvlangKeytreeStack(frame_root); + char stkbuf[512]; + char *stk = kvlangKeytreeStackBuf(frame_root, stkbuf, sizeof stkbuf) + ? stkbuf + : kvlangKeytreeStack(frame_root); if (name[0] == '*') { /* 显式解引用:*[0,±k] → 读 frame_root/[0,±k](ref=1 Ptr)→ target=写槽路径。 * `*[0,±k]` 是 layout 编译期生成的形参引用,帧槽必有调用点写入的 Ptr;读不到 @@ -70,13 +83,28 @@ char *kvlangBuiltinResolveWriteSlot(kvlangKv_t *kv, const char *frame_root, cons abort(); } char *target = kvlangXvaluePtrTarget(&pv); - kvlangXvalueFree(&pv); free(stk); + kvlangXvalueFree(&pv); + if (stk != stkbuf) + free(stk); return target; } - kvlangStrbuf_t o; kvlangStrbufInit(&o); - kvlangStrbufPuts(&o, stk); kvlangStrbufPuts(&o, name); - free(stk); - return kvlangStrbufDetach(&o); + /* 存 stk+name 到栈缓冲后 strdup(返回值须自持);过长才回落 strbuf。 */ + size_t sl = strlen(stk), nl = strlen(name); + char outbuf[640]; + char *r; + if (sl + nl + 1 <= sizeof outbuf) { + memcpy(outbuf, stk, sl); + memcpy(outbuf + sl, name, nl); + outbuf[sl + nl] = 0; + r = strdup(outbuf); + } else { + kvlangStrbuf_t o; kvlangStrbufInit(&o); + kvlangStrbufPuts(&o, stk); kvlangStrbufPuts(&o, name); + r = kvlangStrbufDetach(&o); + } + if (stk != stkbuf) + free(stk); + return r; } /* ── coerce / kvlangDisplay ─────────────────────────────────────────────── */ @@ -145,30 +173,40 @@ void kvlangDisplay(const kvlangXvalue_t *v, char **out) { /* ── frame helper ─────────────────────────────────────────────────── */ int kvlangBuiltinReadInputs(kvlangFrame_t *f, kvlangXvalue_t *out, int cap) { - char *fr = kvlangKeytreeFrameRoot(f->pc); + /* 帧根优先用主循环缓存(免每步 malloc + 扫描)。 */ + char *owned = NULL; + const char *fr = f->frame_root; + if (!fr) { owned = kvlangKeytreeFrameRoot(f->pc); fr = owned; } int n = 0; for (int i = 0; i < f->inst->nr && n < cap; i++) { kvlangBuiltinResolveReadValue(f->kv, fr, f->inst->reads[i].name, &f->inst->reads[i].val, &out[n]); n++; } - free(fr); + free(owned); return n; } void kvlangBuiltinFreeInputs(kvlangXvalue_t *in, int n) { for (int i = 0; i < n; i++) kvlangXvalueFree(&in[i]); } void kvlangBuiltinNextPc(kvlangFrame_t *f) { + char buf[512]; + if (kvlangRwirNextPcBuf(f->pc, buf, sizeof buf)) { + kvlangVthreadAdvance(f, buf, "running"); + return; + } kvlangStrbuf_t npc; kvlangStrbufInit(&npc); kvlangRwirNextPc(f->pc, &npc); - kvlangVthreadSet(f->kv, f->vtid, npc.p, "running"); + kvlangVthreadAdvance(f, npc.p, "running"); kvlangStrbufFree(&npc); } int kvlangBuiltinWriteResult(kvlangFrame_t *f, const kvlangXvalue_t *result) { if (f->inst->nw > 0) { - char *fr = kvlangKeytreeFrameRoot(f->pc); + char *owned = NULL; + const char *fr = f->frame_root; + if (!fr) { owned = kvlangKeytreeFrameRoot(f->pc); fr = owned; } char *key = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); - free(fr); + free(owned); kvlangKvPair_t pair = { key, *result }; char err[256]; kvlangKvSet(f->kv, &pair, 1, err, sizeof err); @@ -187,10 +225,14 @@ int kvlangBuiltinSetErr(kvlangFrame_t *f, const char *fmt, ...) { return -1; } -int kvlangBuiltinExecuteCopy(kvlangKv_t *kv, const char *vtid, const char *pc, kvlangRwirInst_t *inst) { - char *fr = kvlangKeytreeFrameRoot(pc); - kvlangFrame_t f = { kv, vtid, pc, inst }; - if (inst->nr == 0) { free(fr); kvlangBuiltinNextPc(&f); return 0; } +int kvlangBuiltinExecuteCopy(kvlangFrame_t *f) { + kvlangKv_t *kv = f->kv; + kvlangRwirInst_t *inst = f->inst; + /* 帧根优先用主循环缓存;status_known/fb_pc 随 f 透传(走同一套 PC 推进语义)。 */ + char *owned = NULL; + const char *fr = f->frame_root; + if (!fr) { owned = kvlangKeytreeFrameRoot(f->pc); fr = owned; } + if (inst->nr == 0) { free(owned); kvlangBuiltinNextPc(f); return 0; } kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangBuiltinResolveReadValue(kv, fr, inst->reads[0].name, &inst->reads[0].val, &v); for (int i = 0; i < inst->nw; i++) { @@ -205,8 +247,8 @@ int kvlangBuiltinExecuteCopy(kvlangKv_t *kv, const char *vtid, const char *pc, k snprintf(msg, sizeof msg, "TypeError: memhead %s does not exist — declare the container first (e.g. `%s:T = {}`)", b, b); free(b); if (ok != 0) { - kvlangXvalueFree(&v); free(fr); - kvlangVthreadSetError(kv, vtid, pc, msg); + kvlangXvalueFree(&v); free(owned); + kvlangVthreadSetError(kv, f->vtid, f->pc, msg); return -1; } } @@ -217,7 +259,7 @@ int kvlangBuiltinExecuteCopy(kvlangKv_t *kv, const char *vtid, const char *pc, k free(key); } kvlangXvalueFree(&v); - free(fr); - kvlangBuiltinNextPc(&f); + free(owned); + kvlangBuiltinNextPc(f); return 0; } diff --git a/runtime/src/rwir_internal.h b/runtime/src/rwir_internal.h index 6524576c..a952d810 100644 --- a/runtime/src/rwir_internal.h +++ b/runtime/src/rwir_internal.h @@ -20,6 +20,8 @@ bool kvlangBuiltinCharConcat(const kvlangXvalue_t *a, const kvlangXvalue_t *b, kvlangXvalue_t *out); /* ── 各 lib 的 rwir handler 原型(表在 rwir_func.c 引用)────────────── */ +/* 单读参 head(定义在 rwir_xvalue.c,rwir_ndarray.c 复用) */ +int xv_head1(kvlangFrame_t *f, kvspaceHead_t *h); /* 算术 / 位 / cast(rwir_int.c) */ int cmp_int(kvlangScalar_t a, kvlangScalar_t b); int kvlangBuiltinAdd(kvlangFrame_t *f), kvlangBuiltinSub(kvlangFrame_t *f), diff --git a/runtime/src/rwir_vthread.c b/runtime/src/rwir_vthread.c index 2a04f78e..65d30c9f 100644 --- a/runtime/src/rwir_vthread.c +++ b/runtime/src/rwir_vthread.c @@ -79,7 +79,7 @@ int kvlangBuiltinVthreadSleep(kvlangFrame_t *f) { static int set_status(kvlangFrame_t *f, const char *status) { kvlangStrbuf_t npc; kvlangStrbufInit(&npc); kvlangRwirNextPc(f->pc, &npc); - kvlangVthreadSet(f->kv, f->vtid, npc.p, status); + kvlangVthreadAdvance(f, npc.p, status); kvlangStrbufFree(&npc); return 0; } diff --git a/runtime/src/vthread.c b/runtime/src/vthread.c index c24bf976..1688d6a5 100644 --- a/runtime/src/vthread.c +++ b/runtime/src/vthread.c @@ -1,32 +1,105 @@ #include "runtime_internal.h" +/* vthread pc/status key 路径缓存(单条目):运行期同一 vtid 连续多步, + * 免每步重建 2 条 key(strbuf 分配 + 格式化)。key 纯由 vtid 派生、内容恒定, + * 不承载任何执行状态——状态一律每步回读 kvspace(见 kvlangVthreadAdvance)。vtid 变化即失效。 */ +static char g_vt_id[128]; +static bool g_vt_valid = false; +static char *g_vt_pc_key = NULL; +static char *g_vt_status_key = NULL; + +/* 取(必要时重建)vtid 的 pc/status key;vtid 过长则返回 false(调用方退化为逐次构造)。 */ +static bool vt_keys(const char *vtid, const char **pc_key, const char **status_key) { + if (g_vt_valid && strcmp(g_vt_id, vtid) == 0) { + *pc_key = g_vt_pc_key; *status_key = g_vt_status_key; + return true; + } + size_t n = strlen(vtid); + if (n == 0 || n >= sizeof g_vt_id) return false; + memcpy(g_vt_id, vtid, n + 1); + kvlangStrbuf_t b; kvlangStrbufInit(&b); + kvlangKeytreeVthreadPc(vtid, &b); + free(g_vt_pc_key); g_vt_pc_key = kvlangStrbufDetach(&b); + kvlangStrbufInit(&b); + kvlangKeytreeVthreadStatus(vtid, &b); + free(g_vt_status_key); g_vt_status_key = kvlangStrbufDetach(&b); + g_vt_valid = true; + *pc_key = g_vt_pc_key; *status_key = g_vt_status_key; + return true; +} + +/* 取单条 vthread 成员 key(pc / status):命中缓存则借用之,否则构造到调用方 strbuf。 */ +static const char *vt_member_key(const char *vtid, bool status, kvlangStrbuf_t *k) { + const char *pk, *sk; + if (vt_keys(vtid, &pk, &sk)) return status ? sk : pk; + if (status) kvlangKeytreeVthreadStatus(vtid, k); + else kvlangKeytreeVthreadPc(vtid, k); + return k->p; +} + +#define VT_MEMBER_GET(fn, want_status) \ + void fn(kvlangKv_t *kv, const char *vtid, char **out) { \ + *out = NULL; \ + kvlangStrbuf_t k; kvlangStrbufInit(&k); \ + const char *key = vt_member_key(vtid, (want_status), &k); \ + kvlangXvalue_t v; kvlangXvalueZero(&v); \ + kvlangKvGetOne(kv, key, &v); \ + if (!kvlangXvalueNone(&v)) *out = kvlangXvalueValueString(&v); \ + kvlangXvalueFree(&v); \ + kvlangStrbufFree(&k); \ + } + +/* 单读 ‥pc / ‥status:主循环每指令边界各取所需(状态门只读 status), + * 免掉「取一个成员却连带读另一个」的多余后端 Get。 */ +VT_MEMBER_GET(kvlangVthreadPcGet, false) +VT_MEMBER_GET(kvlangVthreadStatusGet, true) + void kvlangVthreadGet(kvlangKv_t *kv, const char *vtid, char **pc, char **status) { - *pc = NULL; *status = NULL; - kvlangStrbuf_t k; kvlangStrbufInit(&k); - kvlangKeytreeVthreadPc(vtid, &k); - kvlangXvalue_t pv; kvlangXvalueZero(&pv); - kvlangKvGetOne(kv, k.p, &pv); - if (!kvlangXvalueNone(&pv)) *pc = kvlangXvalueValueString(&pv); - kvlangXvalueFree(&pv); - kvlangKeytreeVthreadStatus(vtid, &k); - kvlangXvalue_t sv; kvlangXvalueZero(&sv); - kvlangKvGetOne(kv, k.p, &sv); - if (!kvlangXvalueNone(&sv)) *status = kvlangXvalueValueString(&sv); - kvlangXvalueFree(&sv); - kvlangStrbufFree(&k); + kvlangVthreadPcGet(kv, vtid, pc); + kvlangVthreadStatusGet(kv, vtid, status); } void kvlangVthreadSet(kvlangKv_t *kv, const char *vtid, const char *pc, const char *status) { - kvlangStrbuf_t k1, k2; kvlangStrbufInit(&k1); kvlangStrbufInit(&k2); - kvlangKeytreeVthreadPc(vtid, &k1); - kvlangKeytreeVthreadStatus(vtid, &k2); + const char *pk, *sk; + kvlangStrbuf_t k1, k2; + kvlangStrbufInit(&k1); kvlangStrbufInit(&k2); + if (!vt_keys(vtid, &pk, &sk)) { + kvlangKeytreeVthreadPc(vtid, &k1); pk = k1.p; + kvlangKeytreeVthreadStatus(vtid, &k2); sk = k2.p; + } kvlangXvalue_t v1, v2; kvlangXvalueZero(&v1); kvlangXvalueZero(&v2); kvlangXvalueNewCharUtf8(&v1, pc); kvlangXvalueNewCharUtf8(&v2, status); - kvlangKvPair_t pairs[2] = { { k1.p, v1 }, { k2.p, v2 } }; + kvlangKvPair_t pairs[2] = { { (char *)pk, v1 }, { (char *)sk, v2 } }; char err[256]; kvlangKvSet(kv, pairs, 2, err, sizeof err); - kvlangXvalueFree(&v1); kvlangXvalueFree(&v2); kvlangStrbufFree(&k1); kvlangStrbufFree(&k2); + kvlangXvalueFree(&v1); kvlangXvalueFree(&v2); + kvlangStrbufFree(&k1); kvlangStrbufFree(&k2); +} + +/* 循环内 PC/status 推进(见 kvlangFrame_t 注释):先写 kvspace(崩溃恢复 + 唯一事实源)。 + * - PC 恒写:它是本指令自己算出来的新地址;写后经 fb_pc 回传,主循环免「刚写就回读」。 + * - status 只在**与 f->status_known 不同**时写:status_known 是本步开始前从 kvspace 读到的 + * ‥status(源值,非进程内副本)。正常执行期两者都是 "running",每步省 1 次后端 Set; + * 而一旦外部把 ‥status 改成 paused/error,主循环下一步的状态门就会读到并停机—— + * 既不拿副本当依据,也不会把 kvspace 留在与执行不符的状态。 + * 两写都走 kvlangKvSetChar 直写 body,跳过 TLV 编码往返。 */ +void kvlangVthreadAdvance(kvlangFrame_t *f, const char *pc, const char *status) { + const char *pk, *sk; + kvlangStrbuf_t k1, k2; + kvlangStrbufInit(&k1); kvlangStrbufInit(&k2); + if (!vt_keys(f->vtid, &pk, &sk)) { + kvlangKeytreeVthreadPc(f->vtid, &k1); pk = k1.p; + kvlangKeytreeVthreadStatus(f->vtid, &k2); sk = k2.p; + } + kvlangKvSetChar(f->kv, pk, pc); + if (!f->status_known || strcmp(f->status_known, status) != 0) + kvlangKvSetChar(f->kv, sk, status); + kvlangStrbufFree(&k1); kvlangStrbufFree(&k2); + if (f->fb_pc) { + free(*f->fb_pc); + *f->fb_pc = strdup(pc); + } } void kvlangVthreadSetDone(kvlangKv_t *kv, const char *vtid, const char *ret) {