aboutsummaryrefslogtreecommitdiff
path: root/src/compiler/compiler.zc
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 05:55:19 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 05:55:19 +0000
commit05f7b7e09ad503e4eac4f757de0d7329174f6437 (patch)
tree768931192a69eff878d8f149c418fff55b0fdf8e /src/compiler/compiler.zc
parentdocs: archive the frozen stage code as one compressed tarball (diff)
analyzer: close the ownership gaps that stopped the compiler compiling itself
an owned local can now move into a released owner declared in the same or an outer block, and g = m*x lends a local owner to a global until the global is assigned again in the same block. leaving that block, giving the local away, or returning while the lend is open is an error. recasting a plain local now names it and says it has no ownership to recast. methdef and syntax_resident use the supported spellings, so make genome reaches its fixpoint from a clean checkout. runerr.sh reads the first // expect: line, since the include headers moved it off line 1.
Diffstat (limited to 'src/compiler/compiler.zc')
-rw-r--r--src/compiler/compiler.zc80
1 files changed, 74 insertions, 6 deletions
diff --git a/src/compiler/compiler.zc b/src/compiler/compiler.zc
index 2a05c58..c33005d 100644
--- a/src/compiler/compiler.zc
+++ b/src/compiler/compiler.zc
@@ -231,6 +231,10 @@ int here, hdelta, hlimit, ptop;
int code_chunk = rt.CODE_END;
word image_buffer, image_words; int image_active, image_org, image_capacity, image_size;
int nloc, lbase, fdepth, fslots, fchk, fret, live, bxdepth, epichain, fframe, infn;
+//: a global that borrows a local owner through g = m*x. each entry is the global's
+//: symbol, the local, and the block depth of the store; the lend ends when the same
+//: global is assigned again in that block, and is an error if the block can end first.
+word lendrec; int nlend, lendcap, xlend = -1;
int finsym, finfer; // the function being compiled, and whether its owned return is still to be inferred (no .u)
int fn_return_type; // a consistent return type inferred from the function body
//: effects come from the real compiler's uses, stores, returns and call edges; unknown bodies stay conservative.
@@ -2729,9 +2733,62 @@ int lroot(int i) {
while (lstate[i] == S_CHILD) i = lowner[i];
return i;
}
+// lend a local owner to global s for the rest of the current block.
+int lend_add(int s, int i) {
+ if (nlend * 3 + 3 > lendcap) { if (!lendcap) lendcap = 48; else lendcap = lendcap * 2; lendrec = chk_resize(lendrec, nlend * 3, lendcap); }
+ lendrec[nlend * 3] = s; lendrec[nlend * 3 + 1] = i; lendrec[nlend * 3 + 2] = fdepth; nlend++;
+ return 0;
+}
+// remove entry k, keeping the others in order.
+int lend_remove(int k) {
+ if (k + 1 < nlend) rt.copy_w(lendrec + k * 3, lendrec + k * 3 + 3, (nlend - k - 1) * 3);
+ nlend--;
+ return 0;
+}
+// an assignment to global s in the block that lent it ends the lend.
+int lend_clear(int s) {
+ int k = nlend;
+ while (k > 0) { k--; if (lendrec[k * 3] == s && lendrec[k * 3 + 2] == fdepth && lendrec[k * 3 + 1] >= lbase) lend_remove(k); }
+ return 0;
+}
+// report local i still lent to the global of entry k.
+int lend_err(int k, char* msg) {
+ int i = lendrec[k * 3 + 1];
+ if (!fchk || !live || panic || dry) return 0;
+ errat(); rt.put_s(lname[i]); rt.put_s(": is lent to global "); rt.put_s(sname[lendrec[k * 3]]); rt.put_s(msg); errend();
+ return 0;
+}
+// local i is about to be given away or released: no global may still borrow it.
+int lend_check(int i) {
+ int k = 0;
+ while (k < nlend) {
+ if (lendrec[k * 3 + 1] == i && i >= lbase) { lend_err(k, "; assign the global again before giving the local away"); lend_remove(k); }
+ else k++;
+ }
+ return 0;
+}
+// leaving every block at depth dep or deeper: a lend made there must have ended.
+int lend_exit(int dep) {
+ int k = 0;
+ while (k < nlend) {
+ if (lendrec[k * 3 + 2] >= dep && lendrec[k * 3 + 1] >= lbase) lend_err(k, "; assign the global again before the block that lent it ends");
+ k++;
+ }
+ return 0;
+}
+// forget the lends made at depth dep or deeper, or by locals from index from onward.
+int lend_drop(int dep, int from) {
+ int k = 0;
+ while (k < nlend) {
+ if ((lendrec[k * 3 + 2] >= dep && lendrec[k * 3 + 1] >= lbase) || lendrec[k * 3 + 1] >= from) lend_remove(k);
+ else k++;
+ }
+ return 0;
+}
// retire existing aliases together with their owner. dead aliases do not keep later loop snapshots live.
int end_owner(int i) {
int j = lbase; word info = talloc(3);
+ lend_check(i);
info[0] = lname[i]; info[1] = lpos[i]; info[2] = tpack();
local_ended[i] = info[2]; local_end_info[i] = info; lstate[i] = S_DEAD;
while (j < nloc) {
@@ -2795,6 +2852,7 @@ int freevar(int i) {
int exit_vars(int dep, int final) {
int i = nloc, st;
if (!fchk || !live) return 0;
+ lend_exit(dep);
while (i > lbase) {
i--;
if (lhome[i] < dep) continue;
@@ -2868,6 +2926,7 @@ int block_close(int save) {
i = lbase;
unused_check(save);
exit_vars(fdepth, 1);
+ lend_drop(fdepth, save);
alias_end(fdepth, save);
while (i < save) { if (lhome[i] >= fdepth) { lstate[i] = S_DEAD; lhome[i] = fdepth - 1; } i++; }
local_drop(save);
@@ -3107,7 +3166,7 @@ int recast(int c, int t) {
int i, st;
if (c == C_FRESH && t == S_M) return C_PLAIN; // m*f(): the unnamed result is let go on purpose, so it can be discarded
if (!fchk) return c;
- if (c < C_VAR) { cerr("only a named owned object can be recast"); return c; }
+ if (c < C_VAR) { cerr("only a named owned object can be recast; this expression has no owner"); return c; }
i = c - C_VAR;
st = lstate[i];
luse(i);
@@ -3229,7 +3288,7 @@ int assigncheck(int k, int i, int rc) {
if (js == S_BORROW || isalias(j)) { lstate[i] = S_CHILD; lowner[i] = lroot(j); lown[i] = 0; touch(j); return C_PLAIN; }
// replace a released owner by moving a same-scope owner into its empty slot.
// fresh declarations still cannot copy owners; the source and its aliases expire.
- if (st == S_DEAD && lown[i] && js == S_M && lown[j] && lhome[i] == lhome[j]
+ if (st == S_DEAD && lown[i] && js == S_M && lown[j] && lhome[j] >= lhome[i]
&& !local_captured[i] && !local_captured[j]) {
lstate[i] = S_M; lused[i] = 0; lown[i] = 1;
lnsz[i] = lnsz[j]; lleaf[i] = lleaf[j];
@@ -3274,6 +3333,9 @@ int assigncheck(int k, int i, int rc) {
}
// a global, or memory the analyzer doesn't track: the collector takes it
// an owner (d, u, or an m local that owns its object) is transferred as if by u*x
+ if (k == LV_GLOBAL) lend_clear(i);
+ if (j >= 0 && k == LV_GLOBAL && xlend == j && lstate[j] == S_M && lown[j]) { xlend = -1; touch(j); lend_add(i, j); return C_PLAIN; }
+ xlend = -1;
if (j >= 0) {
if (lstate[j] == S_D || lstate[j] == S_U || (lstate[j] == S_M && lown[j])) { ev_put(j, EV_GLOBAL, -1); end_owner(j); luse(j); }
else cerrn(j, "is not an owner, so it can't be stored in a global or in untracked memory (the collector takes it over)");
@@ -4185,7 +4247,7 @@ int methdef(char* sn, char* mn, word q, int stat, int typed, int rty, int rtag)
if (prefix) rt.copy_w(joined + 1, r + 1, 3);
rt.copy_w(joined + 1 + prefix * 3, return_tokens + 1, return_tokens[0] * 3);
rt.copy_w(joined + 1 + (prefix + return_tokens[0]) * 3, r + 1 + (prefix + 1) * 3, (r[0] - prefix - 1) * 3);
- word previous = m*r; r = d*joined; rt.free(return_tokens); rt.free(buffer); rt.free(previous);
+ rt.free(r); r = joined; rt.free(return_tokens); rt.free(buffer);
}
src_push(1, r, 0, 1);
next();
@@ -7084,9 +7146,12 @@ int unary() {
if (pk_tk == T_OP && pk_tv == s_star && pq_gl[0]) {
t = S_M; if (tv == s_u) t = S_U; if (tv == s_d) t = S_D;
next(); expect(s_star);
- c = unary(); rval();
+ c = unary(); place = -1; if (lvk == LV_LOCAL) place = lvi; rval();
lvk = LV_NONE;
- return recast(c, t);
+ if (c < C_VAR && c != C_FRESH && place >= 0 && fchk) { cerrn(place, "isn't an owned object, so there is no ownership to recast"); return c; }
+ c = recast(c, t);
+ if (t == S_M && c >= C_VAR && lstate[c - C_VAR] == S_M && lown[c - C_VAR]) xlend = c - C_VAR;
+ return c;
}
}
if (tk == T_OP && tv == s_lp && caststart()) { // (int)x, (node*)x: same bits, the type changes
@@ -7334,6 +7399,7 @@ int expr() {
next();
a = o3on && k == LV_LOCAL && (ltag[i] & 16) && !larr[i];
if (a) op(O_DROP); // an o3 variable: its address comes after the value, so it never waits on the stack across a branch
+ xlend = -1;
rc = expr(); rval();
if (k != LV_LOCAL) af_escape(xaf);
fcoerce(lt);
@@ -8456,6 +8522,7 @@ int ctx_pop() {
f = R_CTX + ctxsp * CTX_WORDS;
local_drop(f[0]);
fdrop(nloc);
+ lend_drop(1 << 30, f[0]);
lbase = f[1];
fdepth = f[2];
fslots = f[3];
@@ -8484,6 +8551,7 @@ int ctx_pop() {
int fn_begin(int chk, int ret) {
capture_enabled = 0; effect_value = 0; effect_current = 0; borrow_slots = 0; borrow_pending = 0; borrow_depth = 0; borrow_jump = -1; borrow_entry = 0;
if (cmode && svkind && !dry) rt.fill_w(svkind, 0, nsym); xvk = 0;
+ lend_drop(1 << 30, nloc);
lbase = nloc; fdepth = 1; fslots = 0; fchk = chk && check_on; fret = ret; fty = 0; finfer = 0; fn_return_type = 0;
live = 1; bxdepth = 0; epichain = 0; infn = 1; loopbase = lsp; lv = 0; lvk = LV_NONE; xbor = -1;
return 0;
@@ -9168,7 +9236,7 @@ int syntax_resident(int symbol) {
word block = chk_resize(0, 0, 4096);
syntax_host_symbols = chk_resize(0, 0, from); syntax_host_floor = ubase; syntax_host_from = from;
top = block + block[-5] - rt.HDR;
- block[0] = rt.code_blocks; block[1] = block + 3; block[2] = top; rt.code_blocks = m*block;
+ block[0] = rt.code_blocks; block[1] = block + 3; block[2] = top; rt.code_blocks = block;
if (rt.syscall(9, 9, top, 0) < top) fatal("syntax code address reservation failed");
here = block + 3; hdelta = 0; hlimit = top; image_active = 0; vislo = 0;
clone = syntax_host_function(symbol); if (clone >= 0 && (sflag[clone] & F_DEF)) entry = sval[clone];