// ---- setup ---- // the collector's roots for the compiler's half of the memory map: the program // area (globals live between functions), the genome's globals, the input source // frames, the live frame stack and the live data stack. rt.zc declares this // and calls it from gc(). int gc_roots() { int end = ptop; if (end > rt.CODE_END) end = rt.CODE_END; rt.markrange(0, end); if (!hdelta && here > ptop && ptop < rt.CODE_END) { end = here; if (end > rt.CODE_END) end = rt.CODE_END; rt.markrange(ptop, end); } // globals of a unit still compiling rt.markrange(rt.GORG, rt.GORG[6]); rt.markrange(R_IH, R_POOLEND); rt.markrange(sval, sval + nsym); rt.markrange(R_TY, R_TYEND); rt.markrange(R_SRC, R_SRC + R_SRC_cap); rt.gc_frames(); rt.markrange(rt.dsp(), rt.data_top); return 0; } // start-up in two parts. zc_init clears the intern table and interns the // spellings the parser compares against; zc_tables fills the operator and // keyword tables. an operator is a name, a precedence and a code sequence in // the vm's own one-letter mnemonics (see seqasm), so "<=" is "~ < 1 ^": swap, // less-than, flip the bit. int zc_init() { receiver_methods = 0; closure_expected = 0; effect_floor = 0; effect_symbols = 0; effect_edges = 0; effect_requirements = 0; effect_current = 0; pooltop = R_POOL; poolend = R_POOLEND; poolblocks = 0; namecount = 0; rt.fill_w(namehash, 0, hashcap); nsym = 0; nfixu = 0; fixfree = 0; nbin = 0; nun = 0; nasg = 0; nkop = 0; seqtop = 0; lsp = 0; ctxsp = 0; ntd = 0; snaptop = 0; nloc = 0; lbase = 0; npm = 0; npe = 0; rt.fill_w(lkeys, 0, lkeycap); lkeyn = 0; s_lp = intern("("); s_rp = intern(")"); s_lb = intern("{"); s_rb = intern("}"); s_lk = intern("["); s_rk = intern("]"); s_comma = intern(","); s_semi = intern(";"); s_eq = intern("="); s_arrow = intern("->"); s_inc = intern("++"); s_dec = intern("--"); s_star = intern("*"); s_slash = intern("/"); s_amp = intern("&"); s_colon = intern(":"); s_hash = intern("#"); s_minus = intern("-"); s_dot = intern("."); s_q = intern("?"); s_case = intern("case"); s_default = intern("default"); s_andand = intern("&&"); s_oror = intern("||"); s_fn = intern("fn"); s_var = intern("let"); s_const = intern("const"); s_else = intern("else"); s_as = intern("as"); s_unchecked = intern("unchecked"); s_immediate = intern("immediate"); s_m = intern("m"); s_d = intern("d"); s_u = intern("u"); s_static = intern("static"); s_start = intern("run.start"); s_main = intern("main"); s_free = intern("rt.free"); s_recycle = intern("rt.recycle"); s_copy_w = intern("rt.copy_w"); s_do = intern("do"); s_while = intern("while"); s_enum = intern("enum"); s_include = intern("include"); s_memdebug = intern("memdebug"); s_scrub = intern("rt.scrub"); s_alloc = intern("rt.alloc_line"); s_check = intern("rt.checkptr"); s_memset = intern("rt.mem_set"); s_own = intern("rt.own"); s_qnew = intern("run.qnew"); s_fovf = intern("rt.frame_overflow"); s_frame_floor = intern("rt.frame_floor"); s_frame_top = intern("rt.frame_top"); s_frame_node = intern("rt.frame_node"); s_frame_busy = intern("rt.frame_busy"); s_frame_guard = intern("rt.frame_guard"); s_frame_grow = intern("rt.frame_grow"); s_frame_leave = intern("rt.frame_leave"); s_enhance = intern("enhance"); s_micro = intern("micro"); s_vm = intern("vm"); return 0; } // operators and keywords. these are plain tables in memory: a program can // add operators, change precedences or replace keywords with its own. // (the order of the first binary operators matters: cexpr refers to * + - << >> // & | by their index, and the comments mark which ones) int zc_tables() { add_unop("!", "0 $ 0 ~ - | 63 > 1 +"); // index 0 add_unop("!!", "0 $ 0 ~ - | 63 > 0 ~ -"); // index 1: normalize to 0 or 1 add_unop("~", "-1 ^"); // index 2 add_unop("-", "0 ~ -"); add_binop("*", 10, "*"); // the first few indexes are used by cexpr add_binop("/", 10, "idiv"); // native divide, wired in by rt.zc add_binop("%", 10, "imod"); add_binop("+", 9, "+"); // 3 add_binop("-", 9, "-"); // 4 add_binop("<<", 8, "ishl"); // 5 add_binop(">>", 8, ">"); // 6 add_binop("<", 7, "<"); add_binop(">", 7, "~ <"); add_binop("<=", 7, "~ < 1 ^"); add_binop(">=", 7, "< 1 ^"); add_binop("==", 6, "- 0 $ 0 ~ - | 63 > 1 +"); add_binop("!=", 6, "- 0 $ 0 ~ - | 63 > 0 ~ -"); add_binop("&", 5, "&"); // 13 add_binop("^", 4, "^"); add_binop("|", 3, "|"); // 15 add_binop("&&", -1, 0); add_binop("||", -2, 0); addkop(s_eq); addkop(s_arrow); addkop(s_inc); addkop(s_dec); add_kw("let", kw_var, F_STMT); add_kw("if", kw_if, F_STMT); add_kw("while", kw_while, F_STMT); add_kw("for", kw_for, F_STMT); add_kw("do", kw_do, F_STMT); add_kw("enum", kw_enum, F_STMT | F_TOP); add_kw("include", kw_include, F_TOP | F_STMT); add_kw("embed", kw_embed, F_TOP | F_STMT); add_kw("run.method", kw_method, F_TOP | F_STMT); add_kw("run.share_value", kw_share_value, F_EXPR); add_kw("run.syntax", kw_syntax, F_TOP | F_STMT); add_kw("run.statement_hook", kw_statement_hook, F_TOP | F_STMT); add_kw("memdebug", kw_memdebug, F_STMT | F_TOP); add_kw("hints", kw_hints, F_STMT | F_TOP); add_kw("switch", kw_switch, F_STMT); add_kw("break", kw_break, F_STMT); add_kw("continue", kw_continue, F_STMT); add_kw("return", kw_return, F_STMT); add_kw("unchecked", kw_unchecked, F_STMT); add_kw("asm", kw_asm, F_STMT | F_EXPR); add_kw("quote", kw_quote, F_EXPR); add_kw("sizeof", kw_sizeof, F_EXPR); add_kw("typeof", kw_typeof, F_EXPR); add_kw("extern", kw_extern, F_TOP); add_kw("cfunc", kw_cfunc, F_EXPR); add_kw("o3", kw_o3, F_STMT); add_kw("len", kw_len, F_EXPR); add_kw("cap", kw_cap, F_EXPR); add_kw("push", kw_push, F_EXPR); add_kw("pop", kw_pop, F_EXPR); add_kw("resize", kw_resize, F_EXPR); return 0; } // the default map's type records sit in the last 16384 words of the heap. // the type list is the first words of that range, so it cannot be kept aside // as a buddy block: types_init would overwrite the header and the allocator // would hand the range out again. cut the heap off at R_TY until the unit // has been compiled, then put the tail back for the program. int heldk; // the size class of the type area, for the memory map. int type_class() { int k = 0, n = R_TYEND - R_TY; while (k < 31 && rt.pow2[k] < n) k++; if (rt.pow2[k] != n) return 0 - 1; return k; } // keep the type area out of the collector's reach while it is in use. int shield_types() { int k; heldk = 0 - 1; if (R_TY <= rt.HEAP_LO || R_TY >= rt.HEAP_HI || R_TYEND > rt.HEAP_HI) return 0; k = type_class(); if (k < 0) return 0; if (R_TY + rt.pow2[k] != R_TYEND) return 0; if (rt.heap_reset(R_TY)) heldk = k; return 0; } // let the collector see the type area again. int unshield_types() { if (heldk >= 0) { rt.heap_uncut(R_TY, heldk); heldk = 0 - 1; } return 0; } // the genome's entry point, run when the image boots: set up the heap and // the compiler, then either run the language server (a genome that carries it) // or compile the unit on stdin, collect the garbage that compiling made, and // call main(). int start() { int s, f; rt.heap_init(); chk_heap = 1; shield_types(); zc_init(); blob_load(rt.GORG[5], 0); zc_tables(); types_init(); here = 0; hdelta = 0; hlimit = rt.CODE_END; ptop = 0; ubase = nsym; vislo = 0; // a genome that carries the language server runs it instead of compiling stdin s = lookup(intern("lsp.lsp_main")); if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); rt.halt(); } unit_stdin(); if (here > ptop) ptop = here; rt.scrub(); // compilation's stale words would keep quarantined blocks alive rt.gc(); // the type area stays out of the allocator. main() may compile again // (the self-host does) and a later new() would otherwise reuse it. if (mdbg_flags) rt.mem_set(mdbg_flags); // a unit that carries src/lib/std.zc has a libinit to fill argc/argv/envc/envp // with. it runs before main, the same way lsp_main runs instead of stdin. s = lookup(intern("std.libinit")); if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } s = lookup(s_main); if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } rt.halt(); return 0; }