diff options
Diffstat (limited to 'src/tools/zc0.c')
| -rw-r--r-- | src/tools/zc0.c | 97 |
1 files changed, 87 insertions, 10 deletions
diff --git a/src/tools/zc0.c b/src/tools/zc0.c index 446557b..5f5b97d 100644 --- a/src/tools/zc0.c +++ b/src/tools/zc0.c @@ -1,7 +1,8 @@ /* zc0: stage 0 of the bootstrap. a small c compiler for the subset of zc that compiler.zc is written in. it reads zc source on stdin and writes a genome - header (vm words placed at GORG) on stdout, in the same image layout the - resident compiler's image mode produces. no lifetime analysis, no quote, no + image (vm words placed at GORG, as little-endian 64-bit words) on stdout, in + the same image layout the resident compiler's image mode produces. the cores + embed that file with #embed (src/core/genome.h). no lifetime analysis, no quote, no parse words, no block expressions: those only exist in compiler.zc. tags (m, d, -> u) and casts (as u, as d) are parsed and recorded in signatures but not checked. */ @@ -454,12 +455,13 @@ static void strlit(void) /* inline word string: jump over it, push its address * static void bstrlit(void) { int j = here, a, i = 0, k, n = (int)strlen(ts); + const char *b = ts; uint64_t w; lit(0); op(O_JMP); a = here; emit(n); while (i < n) { w = 0; k = 0; - while (k < 8 && i < n) { w |= (uint64_t)(unsigned char)ts[i] << (k * 8); k++; i++; } + while (k < 8 && i < n) { w |= (uint64_t)(unsigned char)b[i] << (k * 8); k++; i++; } emit((int64_t)w); } if ((n & 7) == 0) emit(0); @@ -497,7 +499,7 @@ static void primary(void) else if (((s->sig >> 4) & 15) != 1) die("multiple enhancement outputs need asm"); xs = 0; xf = 0; return; } - if (!strcmp(n, "put_s") && na == 1 && xs) fnref("put_bs"); + if ((!strcmp(n, "put_s") || !strcmp(n, "rt.put_s")) && na == 1 && xs) fnref(n[0] == 'r' ? "rt.put_bs" : "put_bs"); else if (!strcmp(n, "len")) fnref("len_s"); else fnref(n); op(O_CALL); @@ -794,6 +796,77 @@ static void enhdef(void) s = add(n, K_ENH, nenh++); s->sig = np + nout * 16 + d * 256; expect(";"); } +/* embed "path" as [type] name; at the top level: a global holding the file's bytes, + read when the image is built. the path is relative to the working directory. + bstr (the default) packs the bytes; char* keeps one character to a word; a + number takes them little-endian; T[] and T[n] lay them out as the array's + elements. the same rules as the resident compiler's kw_embed. */ +static int embed_width(const char *t, int star) +{ + if (star) return 0; + if (!strcmp(t, "char") || !strcmp(t, "u8") || !strcmp(t, "i8") || !strcmp(t, "byte")) return 1; + if (!strcmp(t, "u16") || !strcmp(t, "i16")) return 2; + if (!strcmp(t, "u32") || !strcmp(t, "i32") || !strcmp(t, "f32")) return 4; + if (!strcmp(t, "int") || !strcmp(t, "word") || !strcmp(t, "bool") || !strcmp(t, "float") || !strcmp(t, "f64")) return 8; + return 0; +} +static void embeddecl(void) +{ + char path[1024], ty[64] = "", *n; int star = 0, arr = -2, w, i, a; long len; unsigned char *buf; FILE *f; + if (tk != T_STR) die("embed takes a path in quotes: embed \"file\" as name;"); + snprintf(path, sizeof path, "%s", ts); next(); + expect("as"); + if (tk == T_ID) { + Lex L; lexsave(&L); next(); + int named = is(";"); lexrest(&L); + if (!named) { + snprintf(ty, sizeof ty, "%s", ts); next(); + while (accept("*")) star++; + if (accept("[")) { arr = -1; if (tk == T_NUM) { arr = (int)tv; next(); } expect("]"); } + } + } + n = ident(); expect(";"); + if (!(f = fopen(path, "rb"))) die("can't read the file named by embed"); + fseek(f, 0, SEEK_END); len = ftell(f); fseek(f, 0, SEEK_SET); + buf = malloc((size_t)len + 1); + if (!buf || fread(buf, 1, (size_t)len, f) != (size_t)len) die("can't read the file named by embed"); + fclose(f); + if (arr == -2 && (!ty[0] || (!strcmp(ty, "bstr") && !star))) { + a = here; add(n, K_GVAR, a); emit(0); img[a - GORG] = here; emit(len); + for (i = 0; i < len; i += 8) { uint64_t u = 0; for (w = 0; w < 8 && i + w < len; w++) u |= (uint64_t)buf[i + w] << (w * 8); emit((int64_t)u); } + if (!(len & 7)) emit(0); + } else if (arr == -2 && !strcmp(ty, "char") && star == 1) { + a = here; add(n, K_GVAR, a); emit(0); img[a - GORG] = here; + for (i = 0; i < len; i++) emit(buf[i]); + emit(0); + } else if (arr == -2) { + uint64_t u = 0; w = embed_width(ty, star); + if (!w) die("embed declares a bstr, a char*, a number or an array of numbers"); + if (len > w) die("the embedded file is larger than the type it is declared as"); + if (!strcmp(ty, "f32") && len != 4) die("an embedded f32 takes exactly 4 bytes"); + if ((!strcmp(ty, "float") || !strcmp(ty, "f64")) && len != 8) die("an embedded float takes exactly 8 bytes"); + for (i = 0; i < len; i++) u |= (uint64_t)buf[i] << (i * 8); + if (ty[0] == 'i' && w < 8 && (u >> (w * 8 - 1) & 1)) u |= ~(uint64_t)0 << (w * 8); + if (!strcmp(ty, "f32")) u = fop_s2f(u); + a = here; add(n, K_GVAR, a); emit((int64_t)u); + } else { + int count; w = embed_width(ty, star); + if (!w) die("embed declares a bstr, a char*, a number or an array of numbers"); + if (len % w) die("the embedded file's size is not a whole number of elements"); + count = (int)(len / w); + if (arr >= 0 && count > arr) die("the embedded file is larger than the array it is declared as"); + if (arr >= 0) count = arr; + a = here; + if (!strcmp(ty, "char")) { for (i = 0; i < count; i++) emit(i < len ? buf[i] : 0); } + else { + long bytes = (long)count * w; + for (i = 0; i < bytes; i += 8) { uint64_t u = 0; for (int k = 0; k < 8 && i + k < bytes; k++) u |= (uint64_t)(i + k < len ? buf[i + k] : 0) << (k * 8); emit((int64_t)u); } + if (!bytes) emit(0); + } + add(n, K_GARR, a); + } + free(buf); +} static void top1(void) { if (accept("namespace")) { @@ -802,6 +875,7 @@ static void top1(void) expect(";"); return; } if (accept("include")) { do { ident(); } while (accept(",")); expect(";"); return; } + if (accept("embed")) { embeddecl(); return; } { if (accept("unchecked")) { top1(); return; } if (accept("fn")) { fndef(0); return; } @@ -898,8 +972,9 @@ static void top(void) int main(int argc, char **argv) { if (argc == 2 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) { - fputs("usage: zc0 [--help|-h] < source.zc > genome.h\n" - "compiles bootstrap source from stdin into a c genome header on stdout.\n" + fputs("usage: zc0 [--help|-h] < source.zc > genome.bin\n" + "compiles bootstrap source from stdin into a genome image on stdout:\n" + "little-endian 64-bit words, the format the cores #embed.\n" "source must define start(); memory layout is selected when zc0 is built.\n" "options:\n --help, -h show this help and exit\n", stdout); return 0; @@ -942,8 +1017,10 @@ int main(int argc, char **argv) } emit(0); img[6] = here; - printf("#define GORG %d\n#define GLEN %d\nstatic const int64_t genome[] = {", GORG, here - GORG); - for (i = 0; i < here - GORG; i++) printf("%s%lld", !i ? "\n" : i % 12 ? "," : ",\n", (long long)img[i]); - printf("\n};\n"); - return 0; + for (i = 0; i < here - GORG; i++) { + unsigned char b[8]; uint64_t u = (uint64_t)img[i]; + for (j = 0; j < 8; j++) b[j] = (unsigned char)(u >> (j * 8)); + if (fwrite(b, 1, 8, stdout) != 8) { fprintf(stderr, "zc0: write failed\n"); return 1; } + } + return fflush(stdout) ? 1 : 0; } |
