aboutsummaryrefslogtreecommitdiff
path: root/src/tools/zc0.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/zc0.c')
-rw-r--r--src/tools/zc0.c97
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;
}