// ---- symbol blob ---- // an image ends with its own symbol table so the genome knows its names: // kind, value, signature, name chars, 0 ... then a 0 kind. // emit the names defined since symbol index from (functions, globals, // constants, enhancements) as the image's trailing symbol table int blob_emit(int from) { int i = from, table, words = 1; word p; int b, k; while (i < nsym) { if (skind[i] == K_DOC) { b = sval[i]; words = words + 2 + (b[0] >> 3); } if ((skind[i] >= K_FN && skind[i] <= K_CONST) || skind[i] == K_ENH || skind[i] == K_DOC) words = words + 4 + rt.len_w(sname[i]); i++; } reserve_code(words); i = from; // the docs first: each one's bytes go in the image here, and its symbol // takes their address (the bytes come after the code, so no code moves) while (i < nsym) { if (skind[i] == K_DOC) { b = sval[i]; sval[i] = here; emit(b[0]); k = 0; while (k < ((b[0] + 7) >> 3)) { emit(b[1 + k]); k++; } if (!(b[0] & 7)) emit(0); } i++; } table = here; i = from; while (i < nsym) { if ((skind[i] >= K_FN && skind[i] <= K_CONST) || skind[i] == K_ENH || skind[i] == K_DOC) { emit(skind[i]); emit(sval[i]); emit(ssig[i]); p = sname[i]; while (*p) { emit(*p); p++; } emit(0); } i++; } emit(0); return table; } // the reverse: read a blob and define its names, so a running genome knows its // own functions by name // hide: leave out the language server's names. functions are entered before // constants and globals in the blob, so the server's functions come after // chk_mark but the runtime's constants and globals come after it too: the // cut is by name, the functions after chk_mark, and everything from the // server's marker const (LSP_SERVER, the first thing src/lsp/server.zc declares) int blob_load(word p, int hide) { int s, n, past = 0, after = 0, skip, mkn = intern("run.chk_mark"), srvn = intern("lsp.LSP_SERVER"); while (*p) { n = intern(p + 3); if (hide && n == srvn) after = 1; skip = 0; if (hide && n == mkn) past = 1; if (!skip) { s = addsym(n, p[0], p[1]); ssig[s] = p[2]; sflag[s] = F_DEF | F_SIG; if (p[0] == K_ENH) { if (p[1] >= nenh) nenh = p[1] + 1; rt.install(p[2] >> 8); } } p = p + 3 + rt.len_w(p + 3) + 1; } return 0; } // the name of the function whose code holds address a, for the frames of a backtrace that have // no line table (rt.frame_namer is set to this when the compiler starts). the nearest function // at or below a, or 0. char* frame_symbol(int a) { int i = 0, best = 0 - 1; while (i < nsym) { if (skind[i] == K_FN && (sflag[i] & F_DEF) && sval[i] <= a && sval[i] > 0 && (best < 0 || sval[i] > sval[best])) best = i; i++; } if (best < 0) return 0; return sname[best]; } // an image built with line tables gets the head of its chain in its own rt.line_blocks, the // global the runtime reads, as that global's initial value. int lt_image_head() { int s = lookup(intern("rt.line_blocks")); if (lt_head && s >= 0 && skind[s] == K_GVAR) *(sval[s] + hdelta) = lt_head; return 0; } // compile the rest of stdin for org into a growing output buffer. the image's // addresses remain logical addresses at org while its temporary storage grows. // image_words and image_size expose the finished image; initial is its first // buffer reservation. the matching host layout must still fit the finished image. int build_image_grow(int org, int initial) { int sh = here, sd = hdelta, sl = hlimit, sub = ubase, svl = vislo, sns = nsym, sen = nenh, n; word saved_head = lt_head; char* saved_file = lt_file; word saved_file_at = lt_file_at; char* saved_mark = lt_mark_file; int saved_base = lt_mark_base, saved_nm = lt_next_mark, saved_nmarks = lt_nmarks; word saved_buffer = image_buffer; int saved_active = image_active, saved_org = image_org, saved_capacity = image_capacity; if (org < 0 || initial < 7 || initial > 0x7fffffff - org) fatal("bad image reservation"); image_buffer = chk_resize(0, 0, initial); image_active = 1; image_org = org; image_capacity = initial; ubase = nsym; vislo = nsym; nenh = 0; here = org; hdelta = image_buffer - org; hlimit = org + initial; lt_head = 0; lt_file_at = 0; lt_mark_file = 0; lt_mark_base = 0; lt_next_mark = 0; lt_nmarks = 0; lit(rt.FTOP); lit(rt.ZFP); op(O_ST); fnref(fwdfn(s_start)); op(O_JMP); emit(0); emit(0); unit_stdin(); lt_image_head(); n = blob_emit(ubase); image_buffer[5] = n; image_buffer[6] = here; n = here - org; image_words = image_buffer; image_size = n; popsyms(sns); ubase = sub; vislo = svl; here = sh; hdelta = sd; hlimit = sl; nenh = sen; lt_head = saved_head; lt_file = saved_file; lt_file_at = saved_file_at; lt_mark_file = saved_mark; lt_mark_base = saved_base; lt_next_mark = saved_nm; lt_nmarks = saved_nmarks; image_buffer = saved_buffer; image_active = saved_active; image_org = saved_org; image_capacity = saved_capacity; return n; } // retain the explicit destination interface for callers that already own storage. // use build_image_grow and image_words when the output must grow independently. int build_image(int org, int dst) { int n = build_image_grow(org, 4096), words = rt.syscall(9, 0, 0, 0); if (dst < 0 || n > words - dst) fatal("image destination too small"); rt.copy_w(dst, image_words, n); return n; }