kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

parse_runner.c (17669B)


      1 /* parse-runner — file-driven C front-end test runner.
      2  *
      3  *   parse-runner --emit   FILE.c OUT.o   # full pipeline → ELF .o
      4  *   parse-runner --emit-c FILE.c OUT.c   # full pipeline → C source via
      5  *                                          --emit=c CGTarget (Phase 1 C
      6  * backend; see doc/CBACKEND.md) parse-runner --jit    FILE.c         # full
      7  * pipeline → JIT, call test_main
      8  *
      9  * Exclusively uses the public kit.h surface: this is the same path real
     10  * driver consumers take. Built once; the shell runner walks
     11  * `test/parse/cases` for `.c` files and invokes one of the two modes per
     12  * case. The JIT
     13  * mode's exit status is `test_main`'s return value (mod 256); the emit
     14  * mode's exit status is 0 on success and nonzero on any failure (the
     15  * shell harness compares the resulting .o against the expected exit code
     16  * via the cg/link harness binaries kit-roundtrip / link-exe-runner /
     17  * jit-runner).
     18  *
     19  * The execmem boilerplate mirrors test/cg/harness/cg_runner.c — strict
     20  * W^X dual mapping on Apple/Linux, single mapping elsewhere. */
     21 
     22 #ifndef _GNU_SOURCE
     23 #define _GNU_SOURCE
     24 #endif
     25 
     26 #include <fcntl.h>
     27 #include <kit/compile.h>
     28 #include <kit/core.h>
     29 #include <kit/jit.h>
     30 #include <kit/link.h>
     31 #include <stdarg.h>
     32 #include <stdint.h>
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <sys/mman.h>
     37 #include <sys/stat.h>
     38 #include <unistd.h>
     39 
     40 #include "lib/kit_test_target.h"
     41 
     42 /* ---- env: heap, diag ---- */
     43 
     44 static void* h_alloc(KitHeap* h, size_t n, size_t a) {
     45   (void)h;
     46   (void)a;
     47   return n ? malloc(n) : NULL;
     48 }
     49 static void* h_realloc(KitHeap* h, void* p, size_t o, size_t n, size_t a) {
     50   (void)h;
     51   (void)o;
     52   (void)a;
     53   return realloc(p, n);
     54 }
     55 static void h_free(KitHeap* h, void* p, size_t n) {
     56   (void)h;
     57   (void)n;
     58   free(p);
     59 }
     60 static KitHeap g_heap = {h_alloc, h_realloc, h_free, NULL};
     61 
     62 static void diag_emit(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc,
     63                       const char* fmt, va_list ap) {
     64   static const char* names[] = {"note", "warning", "error", "fatal"};
     65   (void)s;
     66   fprintf(stderr, "[%u]:%u:%u: %s: ", loc.file_id, loc.line, loc.col, names[k]);
     67   vfprintf(stderr, fmt, ap);
     68   fputc('\n', stderr);
     69 }
     70 static KitDiagSink g_diag = {diag_emit, NULL, 0, 0};
     71 
     72 /* ---- env: execmem (W^X) — copied verbatim from cg_runner ---- */
     73 
     74 #if defined(__APPLE__)
     75 #include <mach/mach.h>
     76 #include <mach/mach_vm.h>
     77 #define XM_DUAL_APPLE 1
     78 #else
     79 #define XM_DUAL_APPLE 0
     80 #endif
     81 #if defined(__linux__)
     82 #include <sys/syscall.h>
     83 #define XM_DUAL_LINUX 1
     84 #else
     85 #define XM_DUAL_LINUX 0
     86 #endif
     87 
     88 static int xm_to_posix(int p) {
     89   int q = 0;
     90   if (p & KIT_PROT_READ) q |= PROT_READ;
     91   if (p & KIT_PROT_WRITE) q |= PROT_WRITE;
     92   if (p & KIT_PROT_EXEC) q |= PROT_EXEC;
     93   return q;
     94 }
     95 #if XM_DUAL_LINUX && defined(__x86_64__) && defined(MAP_32BIT)
     96 #define XM_MAP_32BIT MAP_32BIT
     97 static uintptr_t g_xm_low_runtime_hint = 0x40000000u;
     98 static void* xm_low_runtime_hint(size_t n) {
     99   uintptr_t p = g_xm_low_runtime_hint;
    100   uintptr_t step = (uintptr_t)((n + 0xffffu) & ~(size_t)0xffffu);
    101   if (step < 0x10000u) step = 0x10000u;
    102   g_xm_low_runtime_hint = p + step + 0x10000u;
    103   if (g_xm_low_runtime_hint > 0x78000000u) g_xm_low_runtime_hint = 0x40000000u;
    104   return (void*)p;
    105 }
    106 #elif XM_DUAL_LINUX
    107 #define XM_MAP_32BIT 0
    108 static void* xm_low_runtime_hint(size_t n) {
    109   (void)n;
    110   return NULL;
    111 }
    112 #endif
    113 typedef struct XmTok {
    114   void* w;
    115   void* r;
    116   size_t n;
    117 } XmTok;
    118 static KitStatus xm_reserve_single(size_t n, KitExecMemRegion* out) {
    119   void* p =
    120       mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    121   if (p == MAP_FAILED) return KIT_NOMEM;
    122   out->write = out->runtime = p;
    123   out->size = n;
    124   out->token = NULL;
    125   return KIT_OK;
    126 }
    127 static KitStatus xm_reserve(void* u, size_t n, int p, KitExecMemRegion* out) {
    128   (void)u;
    129   if (!out || !n) return KIT_INVALID;
    130   if (!(p & KIT_PROT_EXEC)) return xm_reserve_single(n, out);
    131 #if XM_DUAL_APPLE
    132   {
    133     void* w =
    134         mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    135     mach_vm_address_t r = 0;
    136     vm_prot_t cur = 0, max = 0;
    137     XmTok* tok;
    138     if (w == MAP_FAILED) return KIT_NOMEM;
    139     if (mach_vm_remap(mach_task_self(), &r, (mach_vm_size_t)n, 0,
    140                       VM_FLAGS_ANYWHERE, mach_task_self(),
    141                       (mach_vm_address_t)(uintptr_t)w, FALSE, &cur, &max,
    142                       VM_INHERIT_NONE) != KERN_SUCCESS) {
    143       munmap(w, n);
    144       return KIT_NOMEM;
    145     }
    146     if (mprotect((void*)(uintptr_t)r, n, PROT_READ) != 0) {
    147       munmap((void*)(uintptr_t)r, n);
    148       munmap(w, n);
    149       return KIT_NOMEM;
    150     }
    151     tok = (XmTok*)malloc(sizeof(*tok));
    152     if (!tok) {
    153       munmap((void*)(uintptr_t)r, n);
    154       munmap(w, n);
    155       return KIT_NOMEM;
    156     }
    157     tok->w = w;
    158     tok->r = (void*)(uintptr_t)r;
    159     tok->n = n;
    160     out->write = w;
    161     out->runtime = (void*)(uintptr_t)r;
    162     out->size = n;
    163     out->token = tok;
    164     return KIT_OK;
    165   }
    166 #elif XM_DUAL_LINUX
    167   {
    168     int fd = (int)syscall(SYS_memfd_create, "kit-jit-test", 0u);
    169     void *w, *r;
    170     XmTok* tok;
    171     if (fd < 0) return KIT_NOMEM;
    172     if (ftruncate(fd, (off_t)n) != 0) {
    173       close(fd);
    174       return KIT_NOMEM;
    175     }
    176     w = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED | XM_MAP_32BIT, fd, 0);
    177     if (w == MAP_FAILED) {
    178       close(fd);
    179       return KIT_NOMEM;
    180     }
    181     r = mmap(xm_low_runtime_hint(n), n, PROT_READ, MAP_SHARED | XM_MAP_32BIT,
    182              fd, 0);
    183     close(fd);
    184     if (r == MAP_FAILED) {
    185       munmap(w, n);
    186       return KIT_NOMEM;
    187     }
    188     tok = (XmTok*)malloc(sizeof(*tok));
    189     if (!tok) {
    190       munmap(r, n);
    191       munmap(w, n);
    192       return KIT_NOMEM;
    193     }
    194     tok->w = w;
    195     tok->r = r;
    196     tok->n = n;
    197     out->write = w;
    198     out->runtime = r;
    199     out->size = n;
    200     out->token = tok;
    201     return KIT_OK;
    202   }
    203 #else
    204   return xm_reserve_single(n, out);
    205 #endif
    206 }
    207 static KitStatus xm_protect(void* u, void* a, size_t n, int p) {
    208   (void)u;
    209   return mprotect(a, n, xm_to_posix(p)) == 0 ? KIT_OK : KIT_IO;
    210 }
    211 static void xm_release(void* u, KitExecMemRegion* region) {
    212   (void)u;
    213   if (!region || !region->size) return;
    214   if (region->token) {
    215     XmTok* tok = (XmTok*)region->token;
    216     if (tok->r && tok->r != tok->w) munmap(tok->r, tok->n);
    217     if (tok->w) munmap(tok->w, tok->n);
    218     free(tok);
    219   } else if (region->write) {
    220     munmap(region->write, region->size);
    221   }
    222   region->write = region->runtime = NULL;
    223   region->size = 0;
    224   region->token = NULL;
    225 }
    226 static void xm_flush(void* u, void* a, size_t n) {
    227   (void)u;
    228 #if defined(__aarch64__) || defined(__arm__) || defined(__riscv)
    229 #if defined(__riscv)
    230   __asm__ __volatile__("fence.i" ::: "memory");
    231 #endif
    232   __builtin___clear_cache((char*)a, (char*)a + n);
    233 #else
    234   (void)a;
    235   (void)n;
    236 #endif
    237 }
    238 static KitExecMem g_execmem = {
    239     16 * 1024, xm_reserve, xm_protect, xm_release, xm_flush, NULL,
    240 };
    241 
    242 /* ---- helpers ---- */
    243 
    244 static void target_from_env(KitTargetSpec* t) {
    245   if (kit_test_target_init(t) != 0) {
    246     fprintf(stderr, "parse-runner: kit_test_target_init failed\n");
    247     exit(2);
    248   }
    249 }
    250 
    251 static int read_file(const char* path, uint8_t** out, size_t* out_len) {
    252   FILE* f = fopen(path, "rb");
    253   long n;
    254   uint8_t* buf;
    255   size_t got;
    256   if (!f) return 1;
    257   if (fseek(f, 0, SEEK_END) != 0) {
    258     fclose(f);
    259     return 1;
    260   }
    261   n = ftell(f);
    262   if (n < 0 || fseek(f, 0, SEEK_SET) != 0) {
    263     fclose(f);
    264     return 1;
    265   }
    266   buf = (uint8_t*)malloc((size_t)n + 1);
    267   if (!buf) {
    268     fclose(f);
    269     return 1;
    270   }
    271   got = fread(buf, 1, (size_t)n, f);
    272   fclose(f);
    273   if (got != (size_t)n) {
    274     free(buf);
    275     return 1;
    276   }
    277   buf[n] = 0;
    278   *out = buf;
    279   *out_len = (size_t)n;
    280   return 0;
    281 }
    282 
    283 static KitStatus test_read_all(void* user, const char* path, KitFileData* out) {
    284   uint8_t* data = NULL;
    285   size_t len = 0;
    286   (void)user;
    287   if (!out || read_file(path, &data, &len) != 0) return KIT_IO;
    288   out->data = data;
    289   out->size = len;
    290   out->token = data;
    291   return KIT_OK;
    292 }
    293 
    294 static void test_release(void* user, KitFileData* d) {
    295   (void)user;
    296   if (!d) return;
    297   free(d->token);
    298   d->data = NULL;
    299   d->size = 0;
    300   d->token = NULL;
    301 }
    302 
    303 static KitFileIO g_file_io = {test_read_all, test_release, NULL, NULL};
    304 
    305 static void ctx_init(KitContext* ctx) {
    306   memset(ctx, 0, sizeof *ctx);
    307   ctx->heap = &g_heap;
    308   ctx->diag = &g_diag;
    309   ctx->file_io = &g_file_io;
    310   ctx->now = -1;
    311 }
    312 
    313 static KitStatus compiler_new_for_target(KitTargetSpec spec, KitContext* ctx,
    314                                          KitTarget** target_out,
    315                                          KitCompiler** compiler_out) {
    316   KitTargetOptions opts;
    317   KitStatus st;
    318   if (target_out) *target_out = NULL;
    319   if (compiler_out) *compiler_out = NULL;
    320   memset(&opts, 0, sizeof opts);
    321   opts.spec = spec;
    322   st = kit_target_new(ctx, &opts, target_out);
    323   if (st != KIT_OK) return st;
    324   st = kit_compiler_new(*target_out, ctx, compiler_out);
    325   if (st != KIT_OK) {
    326     kit_target_free(*target_out);
    327     *target_out = NULL;
    328   }
    329   return st;
    330 }
    331 
    332 static int opt_level_from_env(void) {
    333   const char* s = getenv("KIT_OPT_LEVEL");
    334   if (!s || !*s) return 0;
    335   if (!strcmp(s, "0")) return 0;
    336   if (!strcmp(s, "1")) return 1;
    337   if (!strcmp(s, "2")) return 2;
    338   fprintf(stderr, "parse-runner: invalid KIT_OPT_LEVEL=%s\n", s);
    339   exit(2);
    340 }
    341 
    342 static void add_test_system_includes(KitPreprocessOptions* pp) {
    343   static const char* dirs[] = {"rt/include"};
    344   pp->system_include_dirs = dirs;
    345   pp->nsystem_include_dirs = 1;
    346 }
    347 
    348 static int add_runtime_archive(KitLinkArchiveInput* rt_archive,
    349                                uint8_t** rt_data_out) {
    350   uint8_t* data = NULL;
    351   size_t len = 0;
    352   const char* arch = kit_test_arch_name();
    353   const char* path = "build/rt/aarch64-linux/libkit_rt.a";
    354   if (!strcmp(arch, "rv64") || !strcmp(arch, "riscv64"))
    355     path = "build/rt/riscv64-linux/libkit_rt.a";
    356   else if (!strcmp(arch, "x64") || !strcmp(arch, "x86_64") ||
    357            !strcmp(arch, "amd64"))
    358     path = "build/rt/x86_64-linux/libkit_rt.a";
    359   if (read_file(path, &data, &len) != 0) return 0;
    360   memset(rt_archive, 0, sizeof *rt_archive);
    361   rt_archive->name = kit_slice_cstr(path);
    362   rt_archive->bytes.data = data;
    363   rt_archive->bytes.len = len;
    364   *rt_data_out = data;
    365   return 1;
    366 }
    367 
    368 static KitStatus compile_c_obj(KitCompiler* c, const KitCCompileOptions* opts,
    369                                const KitPreprocessOptions* pp, KitSlice name,
    370                                const KitSlice* in, KitObjBuilder** out) {
    371   KitCompileSessionOptions sopts;
    372   KitCompileSession* session = NULL;
    373   KitSourceInput sin;
    374   KitStatus st;
    375   memset(&sopts, 0, sizeof(sopts));
    376   sopts.lang = KIT_LANG_C;
    377   sopts.compile.code = opts->code;
    378   sopts.compile.diagnostics = opts->diagnostics;
    379   if (pp) sopts.compile.preprocess = *pp;
    380   memset(&sin, 0, sizeof(sin));
    381   sin.name = name;
    382   sin.bytes = *in;
    383   sin.lang = KIT_LANG_C;
    384   st = kit_compile_session_new(c, &sopts, &session);
    385   if (st == KIT_OK) st = kit_compile_session_compile(session, &sin, out);
    386   kit_compile_session_free(session);
    387   return st;
    388 }
    389 
    390 static KitStatus compile_c_emit(KitCompiler* c, const KitCCompileOptions* opts,
    391                                 const KitPreprocessOptions* pp, KitSlice name,
    392                                 const KitSlice* in, KitWriter* w) {
    393   KitObjBuilder* ob = NULL;
    394   KitStatus st = compile_c_obj(c, opts, pp, name, in, &ob);
    395   if (st == KIT_OK && !opts->code.emit_c_source)
    396     st = kit_obj_builder_emit(ob, w);
    397   kit_obj_builder_free(ob);
    398   return st;
    399 }
    400 
    401 /* ---- modes ---- */
    402 
    403 static int mode_emit_impl(const char* src_path, const char* out_path,
    404                           int emit_c) {
    405   uint8_t* src = NULL;
    406   size_t src_len = 0;
    407   KitTargetSpec tgt;
    408   KitContext ctx;
    409   KitTarget* target = NULL;
    410   KitCompiler* c = NULL;
    411   KitSlice in;
    412   KitCCompileOptions opts;
    413   KitPreprocessOptions pp;
    414   KitWriter* w = NULL;
    415   int rc = 0;
    416   size_t len = 0;
    417   const uint8_t* data;
    418   int fd;
    419 
    420   if (read_file(src_path, &src, &src_len)) {
    421     fprintf(stderr, "parse-runner: cannot read %s\n", src_path);
    422     return 2;
    423   }
    424   target_from_env(&tgt);
    425   ctx_init(&ctx);
    426   if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) {
    427     free(src);
    428     return 2;
    429   }
    430   memset(&in, 0, sizeof in);
    431   in.data = src;
    432   in.len = src_len;
    433 
    434   memset(&opts, 0, sizeof opts);
    435   /* --emit-c forces opt_level=0 inside CG anyway, but be explicit so the
    436    * env-driven opt level doesn't surprise. */
    437   opts.code.opt_level = emit_c ? 0 : opt_level_from_env();
    438   memset(&pp, 0, sizeof pp);
    439   add_test_system_includes(&pp);
    440 
    441   (void)kit_writer_mem(&g_heap, &w);
    442   if (emit_c) {
    443     opts.code.emit_c_source = true;
    444     opts.code.c_source_writer = w;
    445   }
    446   if (compile_c_emit(c, &opts, &pp, kit_slice_cstr(src_path), &in, w) !=
    447       KIT_OK) {
    448     kit_writer_close(w);
    449     kit_compiler_free(c);
    450     kit_target_free(target);
    451     free(src);
    452     return 1;
    453   }
    454 
    455   data = kit_writer_mem_bytes(w, &len);
    456   fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    457   if (fd < 0) {
    458     perror(out_path);
    459     rc = 2;
    460   } else {
    461     size_t off = 0;
    462     while (off < len) {
    463       ssize_t k = write(fd, data + off, len - off);
    464       if (k <= 0) {
    465         perror("write");
    466         rc = 2;
    467         break;
    468       }
    469       off += (size_t)k;
    470     }
    471     close(fd);
    472   }
    473   kit_writer_close(w);
    474   kit_compiler_free(c);
    475   kit_target_free(target);
    476   free(src);
    477   return rc;
    478 }
    479 
    480 /* On AArch64 host, set up TLS Local-Exec image before invoking JITed
    481  * code. Mirrors test/cg/harness/cg_runner.c: msr → call() must be
    482  * back-to-back with no libc invocations in between (Darwin libc clobbers
    483  * TPIDR_EL0 via the dyld stub binder). Cases that don't use TLS see the
    484  * lookups fail and the block stays zeroed — harmless. */
    485 #if defined(__aarch64__) || defined(__arm64__)
    486 static char g_tls_block[8192] __attribute__((aligned(16)));
    487 
    488 __attribute__((noinline, no_sanitize("address", "undefined"))) static int
    489 call_with_aarch64_tls(int (*fn)(void), void* tls_block) {
    490   void* old_tp;
    491   int result;
    492   __asm__ volatile("mrs %0, tpidr_el0" : "=r"(old_tp)::"memory");
    493   __asm__ volatile("msr tpidr_el0, %0" ::"r"(tls_block) : "memory");
    494   result = fn();
    495   __asm__ volatile("msr tpidr_el0, %0" ::"r"(old_tp) : "memory");
    496   return result;
    497 }
    498 #endif
    499 
    500 static int mode_jit(const char* src_path) {
    501   uint8_t* src = NULL;
    502   size_t src_len = 0;
    503   KitTargetSpec tgt;
    504   KitContext ctx;
    505   KitTarget* target = NULL;
    506   KitCompiler* c = NULL;
    507   KitSlice in;
    508   KitCCompileOptions opts;
    509   KitPreprocessOptions pp;
    510   KitObjBuilder* ob = NULL;
    511   KitLinkArchiveInput rt_archive;
    512   KitLinkSessionOptions lopts;
    513   KitLinkSession* link = NULL;
    514   KitJitHost jhost;
    515   uint8_t* rt_data = NULL;
    516   KitJit* jit = NULL;
    517   int (*fn)(void);
    518   int result;
    519 
    520   if (read_file(src_path, &src, &src_len)) {
    521     fprintf(stderr, "parse-runner: cannot read %s\n", src_path);
    522     return 2;
    523   }
    524   target_from_env(&tgt);
    525   ctx_init(&ctx);
    526   if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) {
    527     free(src);
    528     return 2;
    529   }
    530   memset(&in, 0, sizeof in);
    531   in.data = src;
    532   in.len = src_len;
    533   memset(&opts, 0, sizeof opts);
    534   opts.code.opt_level = opt_level_from_env();
    535   memset(&pp, 0, sizeof pp);
    536   add_test_system_includes(&pp);
    537 
    538   if (compile_c_obj(c, &opts, &pp, kit_slice_cstr(src_path), &in, &ob) !=
    539           KIT_OK ||
    540       !ob) {
    541     kit_compiler_free(c);
    542     kit_target_free(target);
    543     free(src);
    544     return 1;
    545   }
    546 
    547   memset(&lopts, 0, sizeof lopts);
    548   lopts.output_kind = KIT_LINK_OUTPUT_JIT;
    549   lopts.entry = KIT_SLICE_LIT("test_main");
    550 
    551   memset(&jhost, 0, sizeof jhost);
    552   jhost.execmem = &g_execmem;
    553   lopts.jit_host = &jhost;
    554 
    555   if (!add_runtime_archive(&rt_archive, &rt_data)) {
    556     kit_compiler_free(c);
    557     kit_target_free(target);
    558     free(src);
    559     return 1;
    560   }
    561   if (kit_link_session_new(c, &lopts, &link) != KIT_OK ||
    562       kit_link_session_add_obj(link, ob) != KIT_OK ||
    563       kit_link_session_add_archive_bytes(link, &rt_archive) != KIT_OK ||
    564       kit_link_session_jit(link, &jit) != KIT_OK || !jit) {
    565     kit_link_session_free(link);
    566     kit_compiler_free(c);
    567     kit_target_free(target);
    568     free(rt_data);
    569     free(src);
    570     return 1;
    571   }
    572   kit_link_session_free(link);
    573 
    574   fn = (int (*)(void))kit_jit_lookup(jit, KIT_SLICE_LIT("test_main"));
    575 
    576 #if defined(__aarch64__) || defined(__arm64__)
    577   {
    578     char* td_start = (char*)kit_jit_lookup(jit, KIT_SLICE_LIT("__tdata_start"));
    579     char* td_end = (char*)kit_jit_lookup(jit, KIT_SLICE_LIT("__tdata_end"));
    580     unsigned long bs_n = (unsigned long)(unsigned long long)kit_jit_lookup(
    581         jit, KIT_SLICE_LIT("__tbss_size"));
    582     if (td_start && td_end) {
    583       unsigned long td_n = (unsigned long)(td_end - td_start);
    584       unsigned long i;
    585       for (i = 0; i < td_n; ++i) g_tls_block[16 + i] = td_start[i];
    586       for (i = 0; i < bs_n; ++i) g_tls_block[16 + td_n + i] = 0;
    587     }
    588   }
    589 #endif
    590 
    591   if (fn) {
    592 #if defined(__aarch64__) || defined(__arm64__)
    593     result = call_with_aarch64_tls(fn, g_tls_block);
    594 #else
    595     result = fn();
    596 #endif
    597   } else {
    598     result = 1;
    599   }
    600 
    601   kit_jit_free(jit);
    602   kit_compiler_free(c);
    603   kit_target_free(target);
    604   free(rt_data);
    605   free(src);
    606   return result;
    607 }
    608 
    609 static int usage(void) {
    610   fprintf(stderr,
    611           "usage: parse-runner --emit   FILE.c OUT.o\n"
    612           "       parse-runner --emit-c FILE.c OUT.c\n"
    613           "       parse-runner --jit    FILE.c\n");
    614   return 2;
    615 }
    616 
    617 int main(int argc, char** argv) {
    618   long ps = sysconf(_SC_PAGESIZE);
    619   if (ps > 0) g_execmem.page_size = (size_t)ps;
    620   if (argc < 2) return usage();
    621   if (!strcmp(argv[1], "--emit") && argc == 4)
    622     return mode_emit_impl(argv[2], argv[3], 0);
    623   if (!strcmp(argv[1], "--emit-c") && argc == 4)
    624     return mode_emit_impl(argv[2], argv[3], 1);
    625   if (!strcmp(argv[1], "--jit") && argc == 3) return mode_jit(argv[2]);
    626   return usage();
    627 }