From ecc350ca5c2eb50be8309c5f14f8bf0d87db9a02 Mon Sep 17 00:00:00 2001 From: Moon Flower Date: Mon, 12 Oct 2026 08:46:23 +0330 Subject: a --- src/tools/genmap.c | 858 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 858 insertions(+) create mode 100644 src/tools/genmap.c (limited to 'src/tools/genmap.c') diff --git a/src/tools/genmap.c b/src/tools/genmap.c new file mode 100644 index 0000000..dc1971c --- /dev/null +++ b/src/tools/genmap.c @@ -0,0 +1,858 @@ +/* one layout for every memory size. addresses in a genome are literals, so + each size is its own image. this tool writes that image's memmap.h and + the runtime (and, for a compiler build, compiler.zc) with the same const + names as the checked-in 1,048,576-word sources. + + run from the repo root: + + cc -O2 src/tools/genmap.c -o genmap + ./genmap --preset small -o build/small + ./genmap --preset run -o build/run + ./genmap --preset default --check + + ./genmap --mode compiler --heap-lo 8192 --heap 20480 --types 8192 --genome 245760 --frame 2048 --return 512 --mark 512 --data 3072 --snap-cap 1000 --quote-cap 300 -o build/small + ./genmap --mode run --words 20480 -o build/run + + --preset default puts the code area, the compiler tables, the genome and + the stacks first and the heap last, so the heap can grow at run time up to + the total memory size (PW is where the fixed part ends; the host's MW is + the total). --preset fixed is the historical 1M map (a fixed heap below + the frame pointer, types at the top of it). --mode packs from the bottom. omitting + --words rounds MW up to a multiple of 4096, which is what the arm64 jit + needs in order to encode cmp xn, #MW. the checked-in sources stay the + default map; --check fails if they drift from that preset. */ +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif + +static int align_up(int x, int a) { return (x + a - 1) & ~(a - 1); } + +static void die(const char *m) { fprintf(stderr, "genmap: %s\n", m); exit(1); } + +static int parse_num(const char *s) +{ + char *e; + long v; + if (!s || !*s) die("missing number"); + v = strtol(s, &e, 0); + if (*e || v <= 0 || v > 0x7fffffff) die("bad number"); + return (int)v; +} + +static char *slurp(const char *path) +{ + FILE *f = fopen(path, "rb"); + long n; char *b; + if (!f) { perror(path); exit(1); } + if (fseek(f, 0, SEEK_END) || (n = ftell(f)) < 0 || fseek(f, 0, SEEK_SET)) die("seek"); + b = malloc((size_t)n + 1); + if (!b || fread(b, 1, (size_t)n, f) != (size_t)n) die("read"); + b[n] = 0; + fclose(f); + return b; +} + +static void emit_file(const char *path, const char *s) +{ + FILE *f = fopen(path, "wb"); + if (!f) { perror(path); exit(1); } + fputs(s, f); + if (fclose(f)) die("write"); +} + +/* replace the text between the line that is `begin` and the next line that + is `end`. the marker lines stay. */ +static char *splice(const char *src, const char *begin, const char *end, const char *repl) +{ + const char *a = strstr(src, begin), *b; + char *out; size_t n, nr; + if (!a) { fprintf(stderr, "genmap: missing %s\n", begin); exit(1); } + a += strlen(begin); + if (*a == '\n') a++; + b = strstr(a, end); + if (!b) { fprintf(stderr, "genmap: missing %s\n", end); exit(1); } + nr = strlen(repl); + n = (size_t)(a - src) + nr + strlen(b) + 1; + out = malloc(n); + if (!out) die("oom"); + memcpy(out, src, (size_t)(a - src)); + memcpy(out + (a - src), repl, nr); + strcpy(out + (a - src) + nr, b); + return out; +} + +static char *replace_all(const char *src, const char *from, const char *to) +{ + const char *p = src, *hit; + size_t nf = strlen(from), nt = strlen(to), n = 0, count = 0; + char *out, *w; + while ((hit = strstr(p, from))) { count++; p = hit + nf; } + if (!count) { fprintf(stderr, "genmap: missing %s\n", from); exit(1); } + out = malloc(strlen(src) + count * (nt - nf) + 1); + if (!out) die("oom"); + p = src; w = out; + while ((hit = strstr(p, from))) { + memcpy(w, p, (size_t)(hit - p)); w += hit - p; + memcpy(w, to, nt); w += nt; + p = hit + nf; + n++; + } + strcpy(w, p); + (void)n; + return out; +} + +typedef struct { + int MW, HEAP_LO, HEAP_HI, ZFP, GORG, FBOT, FTOP, RS0, MARK0, MARKN, DSLO, DSHI; + int TW, CODE_END, GROW; /* TW: total words (MW is where the fixed map ends); CODE_END: program words end */ + int IHN, R_IH, R_POOL, R_POOLEND, NS, R_SYM; + int sname, skind, sval, ssig, sflag, sfix, snext; + int NL, lname, lslot, lhome, lstate, lowner, lused, larr, lown; + int NF, fsite, fnext, R_SNAP, R_QBUF, R_SRC, R_SEQ; + int bname, bprec, bseq, blen, uname, useq, ulen, aname, abin, kops; + int R_CTX, R_LOOP, R_ASM, cend, glen_need, typesz; + int ASM_LABELS, ASM_REFS, ASM_WORDS, EXIT_WORDS; + int ENUM_MEMBERS, TAGGED_ENUMS, STRUCT_METHODS, GENERATED_WORDS; + int RANGE_FACTS, JOIN_PATHS, O3_REGIONS; + const char *name; +} Map; + +/* what actually differs between builds. zero means "use the mode default" + except where a preset fills the field itself before layout. */ +typedef struct { + const char *name; + int compiler; + int words; + int heap_lo, heap, types, zfp_pad; + int heap_top, code_end, total; /* heap above the stacks, free to grow up to total words */ + int genome, frame, ret, mark, data; + int ihn, pool, ns, nl, nf, snap, qbuf, src, seq, ctx, loop; + int snap_cap, quote_cap; /* 0 keeps the source caps (8000 and 2700) */ + int binary, unary, assignments, keywords; + int asm_labels, asm_refs, exits; + int enum_members, tagged_enums, struct_methods, generated_words; + int facts, paths, regions; + int type_formula; /* 0 keeps ZFP-16384; else the copy says ZFP-N */ +} Spec; + +static void need(int cond, const char *m) { if (!cond) die(m); } + +static void check_common(const Map *m) +{ + need(m->MW && (m->MW % 4096) == 0, "MW must be a positive multiple of 4096"); + need(m->HEAP_LO >= 256 && m->HEAP_HI > m->HEAP_LO, "heap"); + need(m->HEAP_HI - m->HEAP_LO >= 8 && ((m->HEAP_HI - m->HEAP_LO) & 7) == 0, "heap size must be a multiple of eight words"); + if (m->GROW) { + need(m->HEAP_LO == m->MW, "a growing heap starts where the fixed map ends"); + need(m->TW >= m->HEAP_HI && (m->TW % 4096) == 0, "total memory"); + need(m->CODE_END >= 256 && m->ZFP >= m->CODE_END && m->ZFP + 8 < m->MW, "frame pointer"); + } else { +/* the historical map sets ZFP == HEAP_HI: the type area overlaps the heap */ + need(m->ZFP >= m->HEAP_HI && m->ZFP + 8 < m->MW, "frame pointer"); + } + need(m->GORG > m->ZFP + 8, "genome overlaps the frame pointer"); + need(m->GORG + m->glen_need <= m->FBOT, "genome reservation collides with the frame stack"); + need(m->FBOT + 64 < m->FTOP && m->FTOP == m->RS0, "frame stack"); + need(m->MARK0 >= m->FTOP + 64 && m->MARK0 + m->MARKN <= m->DSLO, "mark stack"); + need(m->DSHI == m->MW - 16 && m->DSHI - m->DSLO >= 1100 + 1024, "data stack"); + need(m->DSLO > m->MARK0, "data stack overlaps the mark stack"); +} + +static void tables_tight(Spec *s) +{ + s->ihn = 2048; s->pool = 16384; s->ns = 1280; s->nl = 512; s->nf = 2048; + s->snap = 1024; s->qbuf = 1024; s->src = 1024; s->seq = 1024; + s->ctx = 1024; s->loop = 512; +} + +/* the default map's tables: room for a large program and for several generic + libraries, in words nobody touches until a program needs them */ +static void tables_big(Spec *s) +{ + s->ihn = 32768; s->pool = 0x40000; s->ns = 16384; s->nl = 512; s->nf = 32768; + s->snap = 16384; s->qbuf = 16384; s->src = 1024; s->seq = 2048; + s->ctx = 2048; s->loop = 1024; +} + +static void tables_historical(Spec *s) +{ + s->ihn = 8192; s->pool = 0x9F00; s->ns = 4096; s->nl = 512; s->nf = 8192; + s->snap = 8192; s->qbuf = 8192; s->src = 1024; s->seq = 2048; + s->ctx = 2048; s->loop = 1024; +} + +static void preset_small(Spec *s) +{ + memset(s, 0, sizeof *s); + s->name = "small"; + s->compiler = 1; + s->heap_top = 1; s->code_end = 8192; s->total = 0x8000000; + s->heap_lo = 8192; s->heap = 20480; s->types = 8192; + s->genome = 245760; s->frame = 2048; s->ret = 512; s->mark = 512; s->data = 3072; + tables_tight(s); + s->snap_cap = 1000; s->quote_cap = 300; s->snap = 1000; s->qbuf = 900; s->type_formula = 8192; +} + +static void preset_run(Spec *s) +{ + memset(s, 0, sizeof *s); + s->name = "run"; + s->heap_top = 1; s->code_end = 512; s->total = 0x8000000; + s->heap_lo = 512; s->heap = 16384; s->zfp_pad = 32; + s->genome = 15600; s->frame = 1024; s->ret = 256; s->mark = 256; s->data = 2560; +} + +static void preset_default(Spec *s) +{ + memset(s, 0, sizeof *s); + s->name = "default"; + s->compiler = 1; + s->heap_top = 1; s->code_end = 0x80000; s->types = 65536; + s->heap = 0x60000; s->total = 0x8000000; + s->genome = 0x50000; s->frame = 0x40000; s->ret = 0x10000; s->mark = 0x8000; + s->data = 0x7FF0; + tables_big(s); +} + +/* the map before the heap moved to the top: a fixed heap between the code and + the frame pointer, the type records at the end of it, 1,081,344 words */ +static void preset_fixed(Spec *s) +{ + memset(s, 0, sizeof *s); + s->name = "fixed"; + s->compiler = 1; + s->words = 0x108000; + s->heap_lo = 0x40000; s->heap = 0x60000; + s->genome = 0x28000; s->frame = 0x10000; s->ret = 0x8000; s->mark = 0x4000; + tables_historical(s); +} + +/* the language server: the compiler's own growing map. the document store, the + diagnostics and the check's sets are heap blocks now, so nothing in the server + is at a fixed address and the heap grows with the documents */ +static void preset_lsp(Spec *s) +{ + preset_default(s); + s->name = "lsp"; +} + +static void mode_defaults(Spec *s, int compiler) +{ + memset(s, 0, sizeof *s); + s->name = compiler ? "compiler" : "run"; + s->compiler = compiler; + if (compiler) { + s->heap_top = 1; s->code_end = 8192; s->total = 0x8000000; + s->heap_lo = 8192; s->heap = 20480; s->types = 8192; s->type_formula = 8192; + s->genome = 245760; s->frame = 2048; s->ret = 512; s->mark = 512; s->data = 3072; + tables_tight(s); + } else { + s->heap_top = 1; s->code_end = 512; s->total = 0x8000000; + s->heap_lo = 512; s->heap = 16384; s->zfp_pad = 32; + s->genome = 15600; s->frame = 1024; s->ret = 256; s->mark = 256; s->data = 2560; + } +} + +/* pack every region from the bottom. a requested MW larger than that keeps + the same bases and gives the spare words to the data stack. */ +static void check_layout_size(const Spec *s) +{ + long long size = (s->heap_top ? s->code_end : s->heap_lo) + (long long)s->heap + s->types + s->zfp_pad; + size += (long long)s->genome + s->frame + s->ret + s->mark + s->data + 8192; + if (s->compiler) { + size += (long long)s->ihn + s->pool + 8LL * s->ns + 10LL * s->nl + 2LL * s->nf; + size += (long long)s->snap + s->qbuf + s->src + s->seq + s->ctx + s->loop; + size += 4LL * (s->binary ? s->binary : 64) + 3LL * (s->unary ? s->unary : 32); + size += 2LL * (s->assignments ? s->assignments : 64) + (s->keywords ? s->keywords : 512); + size += 2LL * (s->asm_labels ? s->asm_labels : 64) + 2LL * (s->asm_refs ? s->asm_refs : 256); + size += s->exits ? s->exits : 2560; + } + need(size <= 0x7fffffff, "layout is too large"); + if (s->words && s->heap_top) need((long long)s->words + s->heap + 4095 <= 0x7fffffff, "heap end is too large"); +} + +static void layout(const Spec *s, Map *m) +{ + int a; + check_layout_size(s); + memset(m, 0, sizeof *m); + m->name = s->name; + m->glen_need = s->genome; + m->typesz = s->types; + if (s->heap_top) { + m->GROW = 1; + m->CODE_END = s->code_end; + m->ZFP = m->CODE_END + s->types + s->zfp_pad; + } else { + m->HEAP_LO = s->heap_lo; + m->HEAP_HI = s->heap_lo + s->heap; + m->CODE_END = m->HEAP_LO; + m->ZFP = m->HEAP_HI + s->types + s->zfp_pad; + } + if (s->compiler) { + m->IHN = s->ihn; m->NS = s->ns; m->NL = s->nl; m->NF = s->nf; + a = m->ZFP + 256; + m->R_IH = a; a += m->IHN; + m->R_POOL = a; a += s->pool; m->R_POOLEND = a; a += 256; + m->R_SYM = m->sname = a; a += m->NS; + m->skind = a; a += m->NS; + m->sval = a; a += m->NS; + m->ssig = a; a += m->NS; + m->sflag = a; a += m->NS; + m->sfix = a; a += m->NS; + m->snext = a; a += m->NS; + m->lname = a; a += m->NL; + m->lslot = a; a += m->NL; + m->lhome = a; a += m->NL; + m->lstate = a; a += m->NL; + m->lowner = a; a += m->NL; + m->lused = a; a += m->NL; + m->larr = a; a += m->NL; + m->lown = a; a += m->NL; + m->fsite = a; a += m->NF; + m->fnext = a; a += m->NF; + m->R_SNAP = a; a += s->snap; + m->R_QBUF = a; a += s->qbuf; + m->R_SRC = a; a += s->src; + m->R_SEQ = a; a += s->seq; + { + int binary = s->binary ? s->binary : 64, unary = s->unary ? s->unary : 32; + int assignments = s->assignments ? s->assignments : 64, keywords = s->keywords ? s->keywords : 512; + m->bname = a; a += binary; + m->bprec = a; a += binary; + m->bseq = a; a += binary; + m->blen = a; a += binary; + m->uname = a; a += unary; + m->useq = a; a += unary; + m->ulen = a; a += unary; + m->aname = a; a += assignments; + m->abin = a; a += assignments; + a += 32; + m->kops = a; a += keywords; + } + m->R_CTX = a; a += s->ctx; + m->R_LOOP = a; a += s->loop; + m->R_ASM = a; + m->ASM_LABELS = s->asm_labels ? s->asm_labels : 64; + m->ASM_REFS = s->asm_refs ? s->asm_refs : 256; + { + long long words = 2LL * m->ASM_LABELS + 2LL * m->ASM_REFS; + need(words <= 0x7fffff00, "assembly reservation is too large"); + m->ASM_WORDS = align_up((int)words, 256); + if (!s->asm_labels && !s->asm_refs) m->ASM_WORDS = 1024; + } + m->EXIT_WORDS = s->exits ? s->exits : 2560; + m->ENUM_MEMBERS = s->enum_members ? s->enum_members : 128; + m->TAGGED_ENUMS = s->tagged_enums ? s->tagged_enums : 16; + m->STRUCT_METHODS = s->struct_methods ? s->struct_methods : 32; + m->GENERATED_WORDS = s->generated_words ? s->generated_words : 8192; + m->RANGE_FACTS = s->facts ? s->facts : 32; m->JOIN_PATHS = s->paths ? s->paths : 64; m->O3_REGIONS = s->regions ? s->regions : 10; + need(m->O3_REGIONS <= 0x7fffffff / 67, "region reservation is too large"); + need(m->ENUM_MEMBERS <= 0x10000000, "enum member reservation is too large"); + m->cend = m->R_ASM + m->ASM_WORDS + m->NL + m->NL + m->NS + m->EXIT_WORDS; + m->GORG = align_up(m->cend + 256, 256); + } else { + m->GORG = align_up(m->ZFP + 64, 256); + } + m->FBOT = m->GORG + m->glen_need; + m->FTOP = m->FBOT + s->frame; + m->RS0 = m->FTOP; + m->MARK0 = m->FTOP + s->ret; + m->MARKN = s->mark; + m->DSLO = m->MARK0 + m->MARKN; + if (s->words) { + m->MW = s->words; + m->DSHI = m->MW - 16; + if (s->data && m->DSHI - m->DSLO < s->data) die("memory size too small for the data stack"); + } else { + m->DSHI = m->DSLO + s->data; + m->MW = align_up(m->DSHI + 16, 4096); + m->DSHI = m->MW - 16; + } + m->TW = m->MW; + if (s->heap_top) { + m->HEAP_LO = m->MW; + m->HEAP_HI = m->HEAP_LO + s->heap; + m->TW = s->total > m->HEAP_HI ? s->total : align_up(m->HEAP_HI, 4096); + } + check_common(m); + if (s->types) need(m->ZFP == (s->heap_top ? m->CODE_END : m->HEAP_HI) + s->types + s->zfp_pad, "type area"); +} + +static void hexdef(char **p, char *end, const char *n, int v) +{ + int k = snprintf(*p, (size_t)(end - *p), "const %s = 0x%X;\n", n, v); + if (k < 0 || *p + k >= end) die("const block"); + *p += k; +} + +static char *rt_block(const Map *m) +{ + char buf[2048], *p = buf; + hexdef(&p, buf + sizeof buf, "MW", m->MW); + hexdef(&p, buf + sizeof buf, "CODE_END", m->CODE_END); + hexdef(&p, buf + sizeof buf, "HEAP_LO", m->HEAP_LO); + hexdef(&p, buf + sizeof buf, "HEAP_HI", m->HEAP_HI); + hexdef(&p, buf + sizeof buf, "HEAP_GROW", m->GROW); + hexdef(&p, buf + sizeof buf, "ZFP", m->ZFP); + hexdef(&p, buf + sizeof buf, "GORG", m->GORG); + hexdef(&p, buf + sizeof buf, "FBOT", m->FBOT); + hexdef(&p, buf + sizeof buf, "FTOP", m->FTOP); + hexdef(&p, buf + sizeof buf, "RS0", m->RS0); + hexdef(&p, buf + sizeof buf, "MARK0", m->MARK0); + hexdef(&p, buf + sizeof buf, "MARKN", m->MARKN); + hexdef(&p, buf + sizeof buf, "DSLO", m->DSLO); + hexdef(&p, buf + sizeof buf, "DSHI", m->DSHI); + hexdef(&p, buf + sizeof buf, "MAGIC", 0x2B7E1516); + return strdup(buf); +} + +static char *cc_block(const Map *m) +{ + char buf[4096], *p = buf; + hexdef(&p, buf + sizeof buf, "IHN", m->IHN); + hexdef(&p, buf + sizeof buf, "R_IH", m->R_IH); + hexdef(&p, buf + sizeof buf, "R_POOL", m->R_POOL); + hexdef(&p, buf + sizeof buf, "R_POOLEND", m->R_POOLEND); + hexdef(&p, buf + sizeof buf, "NS", m->NS); + hexdef(&p, buf + sizeof buf, "R_SYM", m->R_SYM); + hexdef(&p, buf + sizeof buf, "sname_BASE", m->sname); + hexdef(&p, buf + sizeof buf, "skind_BASE", m->skind); + hexdef(&p, buf + sizeof buf, "sval_BASE", m->sval); + hexdef(&p, buf + sizeof buf, "ssig_BASE", m->ssig); + hexdef(&p, buf + sizeof buf, "sflag_BASE", m->sflag); + hexdef(&p, buf + sizeof buf, "sfix_BASE", m->sfix); + hexdef(&p, buf + sizeof buf, "snext_BASE", m->snext); + hexdef(&p, buf + sizeof buf, "NL", m->NL); + hexdef(&p, buf + sizeof buf, "lname_BASE", m->lname); + hexdef(&p, buf + sizeof buf, "lslot_BASE", m->lslot); + hexdef(&p, buf + sizeof buf, "lhome_BASE", m->lhome); + hexdef(&p, buf + sizeof buf, "lstate_BASE", m->lstate); + hexdef(&p, buf + sizeof buf, "lowner_BASE", m->lowner); + hexdef(&p, buf + sizeof buf, "lused_BASE", m->lused); + hexdef(&p, buf + sizeof buf, "larr_BASE", m->larr); + hexdef(&p, buf + sizeof buf, "lown_BASE", m->lown); + hexdef(&p, buf + sizeof buf, "NF", m->NF); + hexdef(&p, buf + sizeof buf, "fsite_BASE", m->fsite); + hexdef(&p, buf + sizeof buf, "fnext_BASE", m->fnext); + hexdef(&p, buf + sizeof buf, "R_SNAP_BASE", m->R_SNAP); + hexdef(&p, buf + sizeof buf, "R_QBUF_BASE", m->R_QBUF); + hexdef(&p, buf + sizeof buf, "R_SRC_BASE", m->R_SRC); + hexdef(&p, buf + sizeof buf, "R_SEQ_BASE", m->R_SEQ); + hexdef(&p, buf + sizeof buf, "bname_BASE", m->bname); + hexdef(&p, buf + sizeof buf, "bprec_BASE", m->bprec); + hexdef(&p, buf + sizeof buf, "bseq_BASE", m->bseq); + hexdef(&p, buf + sizeof buf, "blen_BASE", m->blen); + hexdef(&p, buf + sizeof buf, "uname_BASE", m->uname); + hexdef(&p, buf + sizeof buf, "useq_BASE", m->useq); + hexdef(&p, buf + sizeof buf, "ulen_BASE", m->ulen); + hexdef(&p, buf + sizeof buf, "aname_BASE", m->aname); + hexdef(&p, buf + sizeof buf, "abin_BASE", m->abin); + hexdef(&p, buf + sizeof buf, "kops_BASE", m->kops); + hexdef(&p, buf + sizeof buf, "R_CTX_BASE", m->R_CTX); + hexdef(&p, buf + sizeof buf, "R_LOOP_BASE", m->R_LOOP); + hexdef(&p, buf + sizeof buf, "R_ASM", m->R_ASM); + hexdef(&p, buf + sizeof buf, "ASM_LABELS", m->ASM_LABELS); + hexdef(&p, buf + sizeof buf, "ASM_REFS", m->ASM_REFS); + hexdef(&p, buf + sizeof buf, "ASM_WORDS", m->ASM_WORDS); + hexdef(&p, buf + sizeof buf, "EXIT_WORDS", m->EXIT_WORDS); + hexdef(&p, buf + sizeof buf, "ENUM_MEMBERS", m->ENUM_MEMBERS); + hexdef(&p, buf + sizeof buf, "TAGGED_ENUMS", m->TAGGED_ENUMS); + hexdef(&p, buf + sizeof buf, "STRUCT_METHODS", m->STRUCT_METHODS); + hexdef(&p, buf + sizeof buf, "GENERATED_WORDS", m->GENERATED_WORDS); + hexdef(&p, buf + sizeof buf, "RANGE_FACTS", m->RANGE_FACTS); + hexdef(&p, buf + sizeof buf, "JOIN_PATHS", m->JOIN_PATHS); + hexdef(&p, buf + sizeof buf, "O3_REGIONS", m->O3_REGIONS); + return strdup(buf); +} + +static void write_memmap(const char *dir, const Map *m) +{ + char path[512], buf[4096]; + snprintf(path, sizeof path, "%s/memmap.h", dir); + snprintf(buf, sizeof buf, + "/* generated by src/tools/genmap for the %s profile. do not edit. */\n" + "#ifndef MEMMAP_H\n#define MEMMAP_H\n" + "#define PW 0x%X\n#define CODE_END 0x%X\n#define HEAP_LO 0x%X\n#define HEAP_HI 0x%X\n" + "#define HEAP_GROW %d\n" + "#ifndef MW\n#define MW 0x%X\n#endif\n" + "#if MW < HEAP_HI\n#error MW is below the end of the initial heap\n#endif\n" + "#define ZFP 0x%X\n" + "#ifdef GORG\n#if GORG != 0x%X\n#error genome GORG does not match this map\n#endif\n" + "#else\n#define GORG 0x%X\n#endif\n" + "#define FBOT 0x%X\n#define FTOP 0x%X\n#define RS0 0x%X\n" + "#define MARK0 0x%X\n#define MARKN 0x%X\n" + "#define DSLO 0x%X\n#define DSHI 0x%X\n" + "#define DS0 DSHI\n#define SYSGATE (ZFP + 2)\n" + "#define DSLIM (DSLO + 1100)\n#define RSLIM (MARK0 - 8)\n" + "#endif\n", + m->name, m->MW, m->CODE_END, m->HEAP_LO, m->HEAP_HI, m->GROW, m->TW, m->ZFP, m->GORG, m->GORG, + m->FBOT, m->FTOP, m->RS0, m->MARK0, m->MARKN, m->DSLO, m->DSHI); + emit_file(path, buf); +} + +static char *map_txt(const Map *m) +{ + char buf[2048]; + int n; + n = snprintf(buf, sizeof buf, + "profile %s\nMW %d (%lld bytes)\nprogram 0 .. %d\nheap %d .. %d (%d words)\n", + m->name, m->TW, (long long)m->TW * 8, m->CODE_END, m->HEAP_LO, m->HEAP_HI, m->HEAP_HI - m->HEAP_LO); + if (m->GROW) + n += snprintf(buf + n, sizeof buf - n, "fixed map ends at %d; the heap grows up to %d\n", m->MW, m->TW); + if (m->typesz) + n += snprintf(buf + n, sizeof buf - n, "types %d .. %d (%d words)\n", m->ZFP - m->typesz, m->ZFP, m->typesz); + n += snprintf(buf + n, sizeof buf - n, "ZFP %d SYSGATE %d\n", m->ZFP, m->ZFP + 2); + if (m->R_ASM) { + int ltype = m->R_ASM + m->ASM_WORDS; + int rexit = ltype + m->NL + m->NL + m->NS; + n += snprintf(buf + n, sizeof buf - n, + "compiler tables %d .. %d\nltype %d R_EXIT %d .. %d\n", + m->R_IH, m->cend, ltype, rexit, rexit + m->EXIT_WORDS); + } + snprintf(buf + n, sizeof buf - n, + "GORG %d reserved through %d\nframe %d .. %d\nreturn %d .. %d\nmark %d .. %d (%d)\ndata %d .. %d\n", + m->GORG, m->GORG + m->glen_need, m->FBOT, m->FTOP, m->FTOP, m->MARK0, + m->MARK0, m->MARK0 + m->MARKN, m->MARKN, m->DSLO, m->DSHI); + return strdup(buf); +} + +static void write_map_txt(const char *dir, const char *txt) +{ + char path[512]; + snprintf(path, sizeof path, "%s/map.txt", dir); + emit_file(path, txt); + fputs(txt, stderr); +} + +static void mkdir1(const char *dir) +{ +#ifdef _WIN32 + char tmp[512]; + char *p; + snprintf(tmp, sizeof(tmp), "%s", dir); + for (p = tmp; *p; p++) { + if (*p == '/' || *p == '\\') { + char save = *p; + *p = 0; + if (*tmp) _mkdir(tmp); + *p = save; + } + } + if (_mkdir(dir) && errno != EEXIST) { perror(dir); exit(1); } +#else + if (mkdir(dir, 0755) && errno != EEXIST) { perror(dir); exit(1); } +#endif +} + +static char *between(const char *src, const char *begin, const char *end) +{ + const char *a = strstr(src, begin), *b; + char *out; size_t n; + if (!a) return 0; + a += strlen(begin); + b = strstr(a, end); + if (!b || b < a) return 0; + n = (size_t)(b - a); + out = malloc(n + 1); + if (!out) die("oom"); + memcpy(out, a, n); + out[n] = 0; + return out; +} + +static int token_hex(const char *text, const char *name, int *out) +{ + const char *p = text; + size_t n = strlen(name); + while ((p = strstr(p, name))) { + int before_ok = p == text || !(isalnum((unsigned char)p[-1]) || p[-1] == '_'); + int after_ok = !(isalnum((unsigned char)p[n]) || p[n] == '_'); + if (before_ok && after_ok) { + const char *q = p + n; + while (*q == ' ' || *q == '\t') q++; + if (*q == '=') { q++; while (*q == ' ' || *q == '\t') q++; } + if (q[0] == '0' && (q[1] == 'x' || q[1] == 'X')) { + *out = (int)strtol(q, 0, 16); + return 1; + } + if (isdigit((unsigned char)*q)) { + *out = (int)strtol(q, 0, 10); + return 1; + } + } + p += n; + } + return 0; +} + +static void expect_hex(const char *text, const char *where, const char *name, int want) +{ + int got; + if (!token_hex(text, name, &got) || got != want) { + fprintf(stderr, "genmap: %s %s is 0x%X in the layout", where, name, want); + if (token_hex(text, name, &got)) fprintf(stderr, " and 0x%X in the source", got); + fprintf(stderr, "\n"); + exit(1); + } +} + +/* the lfa immediate the checked-in rt.zc carries: the default map's ZFP */ +#define DEFAULT_ZFP 589824 + +static void check_sources(const Map *m) +{ + char lfa_old[64]; + FILE *mf = fopen("memmap.h", "rb"); + const char *mm_path = mf ? (fclose(mf), "memmap.h") : "src/core/memmap.h"; + char *rt = slurp("src/runtime/rt.zc"), *cc = slurp("src/compiler/compiler.zc"), *mm = slurp(mm_path); + char *rtb = between(rt, "// @map-rt", "// @end-map"); + char *ccb = between(cc, "// @map-cc", "// @end-map"); + static const char *rt_names[] = { + "MW", "CODE_END", "HEAP_LO", "HEAP_HI", "HEAP_GROW", "ZFP", "GORG", "FBOT", "FTOP", "RS0", + "MARK0", "MARKN", "DSLO", "DSHI", "MAGIC", 0 + }; + static const char *cc_names[] = { + "IHN", "R_IH", "R_POOL", "R_POOLEND", "NS", "R_SYM", + "sname_BASE", "skind_BASE", "sval_BASE", "ssig_BASE", "sflag_BASE", "sfix_BASE", "snext_BASE", + "NL", "lname_BASE", "lslot_BASE", "lhome_BASE", "lstate_BASE", "lowner_BASE", "lused_BASE", "larr_BASE", "lown_BASE", + "NF", "fsite_BASE", "fnext_BASE", "R_SNAP_BASE", "R_QBUF_BASE", "R_SRC_BASE", "R_SEQ_BASE", + "bname_BASE", "bprec_BASE", "bseq_BASE", "blen_BASE", "uname_BASE", "useq_BASE", "ulen_BASE", "aname_BASE", "abin_BASE", "kops_BASE", + "R_CTX_BASE", "R_LOOP_BASE", "R_ASM", "ASM_LABELS", "ASM_REFS", "ASM_WORDS", "EXIT_WORDS", "ENUM_MEMBERS", "TAGGED_ENUMS", "STRUCT_METHODS", "GENERATED_WORDS", "RANGE_FACTS", "JOIN_PATHS", "O3_REGIONS", 0 + }; + int rt_vals[] = { + m->MW, m->CODE_END, m->HEAP_LO, m->HEAP_HI, m->GROW, m->ZFP, m->GORG, m->FBOT, m->FTOP, m->RS0, + m->MARK0, m->MARKN, m->DSLO, m->DSHI, 0x2B7E1516 + }; + int cc_vals[] = { + m->IHN, m->R_IH, m->R_POOL, m->R_POOLEND, m->NS, m->R_SYM, + m->sname, m->skind, m->sval, m->ssig, m->sflag, m->sfix, m->snext, + m->NL, m->lname, m->lslot, m->lhome, m->lstate, m->lowner, m->lused, m->larr, m->lown, + m->NF, m->fsite, m->fnext, m->R_SNAP, m->R_QBUF, m->R_SRC, m->R_SEQ, + m->bname, m->bprec, m->bseq, m->blen, m->uname, m->useq, m->ulen, m->aname, m->abin, m->kops, + m->R_CTX, m->R_LOOP, m->R_ASM, m->ASM_LABELS, m->ASM_REFS, m->ASM_WORDS, m->EXIT_WORDS, m->ENUM_MEMBERS, m->TAGGED_ENUMS, m->STRUCT_METHODS, m->GENERATED_WORDS, m->RANGE_FACTS, m->JOIN_PATHS, m->O3_REGIONS + }; + int i; + if (!rtb || !ccb) die("map markers missing from the checked-in sources"); + snprintf(lfa_old, sizeof lfa_old, "ld %d", DEFAULT_ZFP); + if (!strstr(rt, lfa_old)) die("src/runtime/rt.zc no longer has the default lfa immediate"); + if (!strstr(cc, "ZFP - 16384")) die("src/compiler/compiler.zc lost its initial type-area formula"); + for (i = 0; rt_names[i]; i++) expect_hex(rtb, "src/runtime/rt.zc", rt_names[i], rt_vals[i]); + for (i = 0; rt_names[i]; i++) { + if (!strcmp(rt_names[i], "MAGIC") || !strcmp(rt_names[i], "HEAP_GROW")) continue; + expect_hex(mm, mm_path, !strcmp(rt_names[i], "MW") ? "PW" : rt_names[i], rt_vals[i]); + } + if (m->R_ASM) for (i = 0; cc_names[i]; i++) expect_hex(ccb, "src/compiler/compiler.zc", cc_names[i], cc_vals[i]); + free(rt); free(cc); free(mm); free(rtb); free(ccb); +} + +static void write_sources(const char *dir, const Spec *s, const Map *m) +{ + char path[512], lfa[64], formula[64], snap[64], quote[64]; + char *rt, *block, *spliced, *done; + rt = slurp("src/runtime/rt.zc"); + block = rt_block(m); + spliced = splice(rt, "// @map-rt", "// @end-map", block); + snprintf(lfa, sizeof lfa, "ld %d", m->ZFP); + { char old[64]; snprintf(old, sizeof old, "ld %d", DEFAULT_ZFP); done = replace_all(spliced, old, lfa); } + snprintf(path, sizeof path, "%s/rt.zc", dir); + emit_file(path, done); + free(rt); free(block); free(spliced); free(done); + if (!s->compiler) return; + { + char *cc = slurp("src/compiler/compiler.zc"); + block = cc_block(m); + spliced = splice(cc, "// @map-cc", "// @end-map", block); + if (s->type_formula) { + snprintf(formula, sizeof formula, "ZFP - %d", s->type_formula); + done = replace_all(spliced, "ZFP - 16384", formula); + free(spliced); spliced = done; + } + snprintf(path, sizeof path, "%s/compiler.zc", dir); + emit_file(path, spliced); + free(cc); free(block); free(spliced); + } +} + +static void usage_status(int status) +{ + fprintf(stderr, + "usage: genmap --preset small|run|default|fixed|lsp [-o DIR] [--check]\n" + " genmap --mode compiler|run [-o DIR] [--words N] [options]\n" + " genmap small|run DIR\n" + "options:\n" + " --code N --total N --heap N --heap-lo N --types N --zfp-pad N --genome N\n" + " --frame N --return N --mark N --data N\n" + " --hash N --pool N --symbols N --locals N --fwd N\n" + " --snap N --quote N --src N --seq N --ctx N --loop N\n" + " --binary N --unary N --assignments N --keywords N\n" + " --asm-labels N --asm-refs N --exit N\n" + " --enum-members N --tagged-enums N --methods N --generated-text N\n" + " --facts N --paths N --regions N\n" + " --snap-cap N --quote-cap N --name LABEL\n" + "--preset default has a growing heap above the stacks; fixed is the old 1,081,344-word map.\n" + "--grow lets a map with a fixed size get bigger when its genome needs more room.\n" + "--mode packs a compiler or a run-only image; spare words go to the data stack.\n" + "--check compares that layout to memmap.h and the checked-in const blocks.\n"); + exit(status); +} + +static void usage(void) { usage_status(1); } + +static int take(int argc, char **argv, int *i) +{ + if (*i + 1 >= argc) usage(); + return parse_num(argv[++*i]); +} + +int main(int argc, char **argv) +{ + Spec s; Map m; + if (argc == 2 && !strcmp(argv[1], "--help")) usage_status(0); + const char *outdir = 0, *preset = 0, *mode = 0; + int check = 0, grow = 0, i, have = 0; + int total = 0, set_total = 0, set_words = 0, set_heap = 0, set_heap_lo = 0, set_types = 0, set_zfp = 0; + int code = 0, binary = 0, unary = 0, assignments = 0, keywords = 0; + int asm_labels = 0, asm_refs = 0, exits = 0; + int enum_members = 0, tagged_enums = 0, struct_methods = 0, generated_words = 0; + int facts = 0, paths = 0, regions = 0; + int set_genome = 0, set_frame = 0, set_ret = 0, set_mark = 0, set_data = 0; + int words = 0, heap = 0, heap_lo = 0, types = 0, zfp_pad = 0; + int genome = 0, frame = 0, ret = 0, mark = 0, data = 0; + int set_ihn = 0, set_pool = 0, set_ns = 0, set_nl = 0, set_nf = 0; + int set_snap = 0, set_qbuf = 0, set_src = 0, set_seq = 0, set_ctx = 0, set_loop = 0; + int set_snap_cap = 0, set_quote_cap = 0; + int ihn = 0, pool = 0, ns = 0, nl = 0, nf = 0, snap = 0, qbuf = 0, srcn = 0, seq = 0, ctx = 0, loop = 0; + int snap_cap = 0, quote_cap = 0, set_name = 0; + const char *s_name = 0; + char *txt; + + if (argc == 3 && argv[1][0] != '-' && (strcmp(argv[1], "small") == 0 || strcmp(argv[1], "run") == 0)) { + preset = argv[1]; + outdir = argv[2]; + } else { + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "--preset") == 0) { if (++i >= argc) usage(); preset = argv[i]; } + else if (strcmp(argv[i], "--mode") == 0) { if (++i >= argc) usage(); mode = argv[i]; } + else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--out") == 0) { if (++i >= argc) usage(); outdir = argv[i]; } + else if (strcmp(argv[i], "--check") == 0) check = 1; + else if (strcmp(argv[i], "--grow") == 0) grow = 1; + else if (strcmp(argv[i], "--words") == 0) { words = take(argc, argv, &i); set_words = 1; } + else if (strcmp(argv[i], "--total") == 0) { total = take(argc, argv, &i); set_total = 1; } + else if (strcmp(argv[i], "--code") == 0) code = take(argc, argv, &i); + else if (strcmp(argv[i], "--binary") == 0) binary = take(argc, argv, &i); + else if (strcmp(argv[i], "--unary") == 0) unary = take(argc, argv, &i); + else if (strcmp(argv[i], "--assignments") == 0) assignments = take(argc, argv, &i); + else if (strcmp(argv[i], "--keywords") == 0) keywords = take(argc, argv, &i); + else if (strcmp(argv[i], "--asm-labels") == 0) asm_labels = take(argc, argv, &i); + else if (strcmp(argv[i], "--asm-refs") == 0) asm_refs = take(argc, argv, &i); + else if (strcmp(argv[i], "--exit") == 0) exits = take(argc, argv, &i); + else if (strcmp(argv[i], "--enum-members") == 0) enum_members = take(argc, argv, &i); + else if (strcmp(argv[i], "--tagged-enums") == 0) tagged_enums = take(argc, argv, &i); + else if (strcmp(argv[i], "--methods") == 0) struct_methods = take(argc, argv, &i); + else if (strcmp(argv[i], "--generated-text") == 0) generated_words = take(argc, argv, &i); + else if (strcmp(argv[i], "--facts") == 0) facts = take(argc, argv, &i); + else if (strcmp(argv[i], "--paths") == 0) paths = take(argc, argv, &i); + else if (strcmp(argv[i], "--regions") == 0) regions = take(argc, argv, &i); + else if (strcmp(argv[i], "--heap") == 0) { heap = take(argc, argv, &i); set_heap = 1; } + else if (strcmp(argv[i], "--heap-lo") == 0) { heap_lo = take(argc, argv, &i); set_heap_lo = 1; } + else if (strcmp(argv[i], "--types") == 0) { types = take(argc, argv, &i); set_types = 1; } + else if (strcmp(argv[i], "--zfp-pad") == 0) { zfp_pad = take(argc, argv, &i); set_zfp = 1; } + else if (strcmp(argv[i], "--genome") == 0) { genome = take(argc, argv, &i); set_genome = 1; } + else if (strcmp(argv[i], "--frame") == 0) { frame = take(argc, argv, &i); set_frame = 1; } + else if (strcmp(argv[i], "--return") == 0) { ret = take(argc, argv, &i); set_ret = 1; } + else if (strcmp(argv[i], "--mark") == 0) { mark = take(argc, argv, &i); set_mark = 1; } + else if (strcmp(argv[i], "--data") == 0) { data = take(argc, argv, &i); set_data = 1; } + else if (strcmp(argv[i], "--hash") == 0) { ihn = take(argc, argv, &i); set_ihn = 1; } + else if (strcmp(argv[i], "--pool") == 0) { pool = take(argc, argv, &i); set_pool = 1; } + else if (strcmp(argv[i], "--symbols") == 0) { ns = take(argc, argv, &i); set_ns = 1; } + else if (strcmp(argv[i], "--locals") == 0) { nl = take(argc, argv, &i); set_nl = 1; } + else if (strcmp(argv[i], "--fwd") == 0) { nf = take(argc, argv, &i); set_nf = 1; } + else if (strcmp(argv[i], "--snap") == 0) { snap = take(argc, argv, &i); set_snap = 1; } + else if (strcmp(argv[i], "--quote") == 0) { qbuf = take(argc, argv, &i); set_qbuf = 1; } + else if (strcmp(argv[i], "--src") == 0) { srcn = take(argc, argv, &i); set_src = 1; } + else if (strcmp(argv[i], "--seq") == 0) { seq = take(argc, argv, &i); set_seq = 1; } + else if (strcmp(argv[i], "--ctx") == 0) { ctx = take(argc, argv, &i); set_ctx = 1; } + else if (strcmp(argv[i], "--loop") == 0) { loop = take(argc, argv, &i); set_loop = 1; } + else if (strcmp(argv[i], "--snap-cap") == 0) { snap_cap = take(argc, argv, &i); set_snap_cap = 1; } + else if (strcmp(argv[i], "--quote-cap") == 0) { quote_cap = take(argc, argv, &i); set_quote_cap = 1; } + else if (strcmp(argv[i], "--name") == 0) { if (++i >= argc) usage(); s_name = argv[i]; set_name = 1; } + else usage(); + } + } + if (preset && mode) die("pass --preset or --mode, not both"); + if (!preset && !mode) usage(); + if (preset) { + if (strcmp(preset, "small") == 0) preset_small(&s); + else if (strcmp(preset, "run") == 0) preset_run(&s); + else if (strcmp(preset, "default") == 0) preset_default(&s); + else if (strcmp(preset, "lsp") == 0) preset_lsp(&s); + else if (strcmp(preset, "fixed") == 0) preset_fixed(&s); + else die("preset is small, run, default, fixed or lsp"); + } else { + if (strcmp(mode, "compiler") == 0) mode_defaults(&s, 1); + else if (strcmp(mode, "run") == 0) mode_defaults(&s, 0); + else die("mode is compiler or run"); + } + if (set_words) s.words = words; + if (set_total) s.total = total; + if (set_heap) s.heap = heap; + if (set_heap_lo) { s.heap_lo = heap_lo; if (s.heap_top) s.code_end = heap_lo; } + if (code) { if (s.heap_top) s.code_end = code; else s.heap_lo = code; } + s.binary = binary; s.unary = unary; s.assignments = assignments; s.keywords = keywords; + s.asm_labels = asm_labels; s.asm_refs = asm_refs; s.exits = exits; + s.enum_members = enum_members; s.tagged_enums = tagged_enums; s.struct_methods = struct_methods; s.generated_words = generated_words; + s.facts = facts; s.paths = paths; s.regions = regions; + if (set_types) { s.types = types; s.type_formula = types == 16384 ? 0 : types; } + if (set_zfp) s.zfp_pad = zfp_pad; + if (set_genome) s.genome = genome; + if (set_frame) s.frame = frame; + if (set_ret) s.ret = ret; + if (set_mark) s.mark = mark; + if (set_data) s.data = data; + if (set_ihn) s.ihn = ihn; + if (set_pool) s.pool = pool; + if (set_ns) s.ns = ns; + if (set_nl) s.nl = nl; + if (set_nf) s.nf = nf; + if (set_snap) s.snap = snap; + if (set_qbuf) s.qbuf = qbuf; + if (set_src) s.src = srcn; + if (set_seq) s.seq = seq; + if (set_ctx) s.ctx = ctx; + if (set_loop) s.loop = loop; + if (set_snap_cap) s.snap = snap_cap; + if (set_quote_cap) { if (quote_cap > 0x7fffffff / 3) die("quote size overflow"); s.qbuf = quote_cap * 3; } + if (set_name) s.name = s_name; + /* --grow: a preset with a fixed memory size gets as many words as its layout needs. the + fixed size stays when it is big enough, so a program that fits builds the same map as + before and a bigger genome moves the top of memory up instead of failing */ + if (grow && s.words) { + Spec t = s; Map probe; + t.words = 0; + if (!t.data) t.data = 16368; + layout(&t, &probe); + if (probe.MW > s.words) s.words = probe.MW; + } + if (s.compiler && (!s.ihn || !s.pool || !s.ns || !s.nl || !s.nf)) die("compiler tables are empty"); + layout(&s, &m); + txt = map_txt(&m); + if (check) check_sources(&m); + if (outdir) { + mkdir1(outdir); + write_memmap(outdir, &m); + write_map_txt(outdir, txt); + write_sources(outdir, &s, &m); + have = 1; + } + if (!have && !check) usage(); + if (check && !outdir) fputs(txt, stdout); + free(txt); + return 0; +} -- cgit v1.2.3