kit

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

parse_runner.c (17880B)


      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   /* Thread the ISA/ABI the resolver needs to honor the spec's float_abi (rv32
    323    * -> ilp32f), so the emitted object's e_flags match the bare harness. */
    324   kit_test_target_isa_abi(&spec, &opts.isa, &opts.abi);
    325   st = kit_target_new(ctx, &opts, target_out);
    326   if (st != KIT_OK) return st;
    327   st = kit_compiler_new(*target_out, ctx, compiler_out);
    328   if (st != KIT_OK) {
    329     kit_target_free(*target_out);
    330     *target_out = NULL;
    331   }
    332   return st;
    333 }
    334 
    335 static int opt_level_from_env(void) {
    336   const char* s = getenv("KIT_OPT_LEVEL");
    337   if (!s || !*s) return 0;
    338   if (!strcmp(s, "0")) return 0;
    339   if (!strcmp(s, "1")) return 1;
    340   if (!strcmp(s, "2")) return 2;
    341   fprintf(stderr, "parse-runner: invalid KIT_OPT_LEVEL=%s\n", s);
    342   exit(2);
    343 }
    344 
    345 static void add_test_system_includes(KitPreprocessOptions* pp) {
    346   static const char* dirs[] = {"rt/include"};
    347   pp->system_include_dirs = dirs;
    348   pp->nsystem_include_dirs = 1;
    349 }
    350 
    351 static int add_runtime_archive(KitLinkArchiveInput* rt_archive,
    352                                uint8_t** rt_data_out) {
    353   uint8_t* data = NULL;
    354   size_t len = 0;
    355   const char* arch = kit_test_arch_name();
    356   const char* path = "build/rt/aarch64-linux/libkit_rt.a";
    357   if (!strcmp(arch, "rv64") || !strcmp(arch, "riscv64"))
    358     path = "build/rt/riscv64-linux/libkit_rt.a";
    359   else if (!strcmp(arch, "x64") || !strcmp(arch, "x86_64") ||
    360            !strcmp(arch, "amd64"))
    361     path = "build/rt/x86_64-linux/libkit_rt.a";
    362   if (read_file(path, &data, &len) != 0) return 0;
    363   memset(rt_archive, 0, sizeof *rt_archive);
    364   rt_archive->name = kit_slice_cstr(path);
    365   rt_archive->bytes.data = data;
    366   rt_archive->bytes.len = len;
    367   *rt_data_out = data;
    368   return 1;
    369 }
    370 
    371 static KitStatus compile_c_obj(KitCompiler* c, const KitCCompileOptions* opts,
    372                                const KitPreprocessOptions* pp, KitSlice name,
    373                                const KitSlice* in, KitObjBuilder** out) {
    374   KitCompileSessionOptions sopts;
    375   KitCompileSession* session = NULL;
    376   KitSourceInput sin;
    377   KitStatus st;
    378   memset(&sopts, 0, sizeof(sopts));
    379   sopts.lang = KIT_LANG_C;
    380   sopts.compile.code = opts->code;
    381   sopts.compile.diagnostics = opts->diagnostics;
    382   if (pp) sopts.compile.preprocess = *pp;
    383   memset(&sin, 0, sizeof(sin));
    384   sin.name = name;
    385   sin.bytes = *in;
    386   sin.lang = KIT_LANG_C;
    387   st = kit_compile_session_new(c, &sopts, &session);
    388   if (st == KIT_OK) st = kit_compile_session_compile(session, &sin, out);
    389   kit_compile_session_free(session);
    390   return st;
    391 }
    392 
    393 static KitStatus compile_c_emit(KitCompiler* c, const KitCCompileOptions* opts,
    394                                 const KitPreprocessOptions* pp, KitSlice name,
    395                                 const KitSlice* in, KitWriter* w) {
    396   KitObjBuilder* ob = NULL;
    397   KitStatus st = compile_c_obj(c, opts, pp, name, in, &ob);
    398   if (st == KIT_OK && !opts->code.emit_c_source)
    399     st = kit_obj_builder_emit(ob, w);
    400   kit_obj_builder_free(ob);
    401   return st;
    402 }
    403 
    404 /* ---- modes ---- */
    405 
    406 static int mode_emit_impl(const char* src_path, const char* out_path,
    407                           int emit_c) {
    408   uint8_t* src = NULL;
    409   size_t src_len = 0;
    410   KitTargetSpec tgt;
    411   KitContext ctx;
    412   KitTarget* target = NULL;
    413   KitCompiler* c = NULL;
    414   KitSlice in;
    415   KitCCompileOptions opts;
    416   KitPreprocessOptions pp;
    417   KitWriter* w = NULL;
    418   int rc = 0;
    419   size_t len = 0;
    420   const uint8_t* data;
    421   int fd;
    422 
    423   if (read_file(src_path, &src, &src_len)) {
    424     fprintf(stderr, "parse-runner: cannot read %s\n", src_path);
    425     return 2;
    426   }
    427   target_from_env(&tgt);
    428   ctx_init(&ctx);
    429   if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) {
    430     free(src);
    431     return 2;
    432   }
    433   memset(&in, 0, sizeof in);
    434   in.data = src;
    435   in.len = src_len;
    436 
    437   memset(&opts, 0, sizeof opts);
    438   /* --emit-c forces opt_level=0 inside CG anyway, but be explicit so the
    439    * env-driven opt level doesn't surprise. */
    440   opts.code.opt_level = emit_c ? 0 : opt_level_from_env();
    441   memset(&pp, 0, sizeof pp);
    442   add_test_system_includes(&pp);
    443 
    444   (void)kit_writer_mem(&g_heap, &w);
    445   if (emit_c) {
    446     opts.code.emit_c_source = true;
    447     opts.code.c_source_writer = w;
    448   }
    449   if (compile_c_emit(c, &opts, &pp, kit_slice_cstr(src_path), &in, w) !=
    450       KIT_OK) {
    451     kit_writer_close(w);
    452     kit_compiler_free(c);
    453     kit_target_free(target);
    454     free(src);
    455     return 1;
    456   }
    457 
    458   data = kit_writer_mem_bytes(w, &len);
    459   fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    460   if (fd < 0) {
    461     perror(out_path);
    462     rc = 2;
    463   } else {
    464     size_t off = 0;
    465     while (off < len) {
    466       ssize_t k = write(fd, data + off, len - off);
    467       if (k <= 0) {
    468         perror("write");
    469         rc = 2;
    470         break;
    471       }
    472       off += (size_t)k;
    473     }
    474     close(fd);
    475   }
    476   kit_writer_close(w);
    477   kit_compiler_free(c);
    478   kit_target_free(target);
    479   free(src);
    480   return rc;
    481 }
    482 
    483 /* On AArch64 host, set up TLS Local-Exec image before invoking JITed
    484  * code. Mirrors test/cg/harness/cg_runner.c: msr → call() must be
    485  * back-to-back with no libc invocations in between (Darwin libc clobbers
    486  * TPIDR_EL0 via the dyld stub binder). Cases that don't use TLS see the
    487  * lookups fail and the block stays zeroed — harmless. */
    488 #if defined(__aarch64__) || defined(__arm64__)
    489 static char g_tls_block[8192] __attribute__((aligned(16)));
    490 
    491 __attribute__((noinline, no_sanitize("address", "undefined"))) static int
    492 call_with_aarch64_tls(int (*fn)(void), void* tls_block) {
    493   void* old_tp;
    494   int result;
    495   __asm__ volatile("mrs %0, tpidr_el0" : "=r"(old_tp)::"memory");
    496   __asm__ volatile("msr tpidr_el0, %0" ::"r"(tls_block) : "memory");
    497   result = fn();
    498   __asm__ volatile("msr tpidr_el0, %0" ::"r"(old_tp) : "memory");
    499   return result;
    500 }
    501 #endif
    502 
    503 static int mode_jit(const char* src_path) {
    504   uint8_t* src = NULL;
    505   size_t src_len = 0;
    506   KitTargetSpec tgt;
    507   KitContext ctx;
    508   KitTarget* target = NULL;
    509   KitCompiler* c = NULL;
    510   KitSlice in;
    511   KitCCompileOptions opts;
    512   KitPreprocessOptions pp;
    513   KitObjBuilder* ob = NULL;
    514   KitLinkArchiveInput rt_archive;
    515   KitLinkSessionOptions lopts;
    516   KitLinkSession* link = NULL;
    517   KitJitHost jhost;
    518   uint8_t* rt_data = NULL;
    519   KitJit* jit = NULL;
    520   int (*fn)(void);
    521   int result;
    522 
    523   if (read_file(src_path, &src, &src_len)) {
    524     fprintf(stderr, "parse-runner: cannot read %s\n", src_path);
    525     return 2;
    526   }
    527   target_from_env(&tgt);
    528   ctx_init(&ctx);
    529   if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) {
    530     free(src);
    531     return 2;
    532   }
    533   memset(&in, 0, sizeof in);
    534   in.data = src;
    535   in.len = src_len;
    536   memset(&opts, 0, sizeof opts);
    537   opts.code.opt_level = opt_level_from_env();
    538   memset(&pp, 0, sizeof pp);
    539   add_test_system_includes(&pp);
    540 
    541   if (compile_c_obj(c, &opts, &pp, kit_slice_cstr(src_path), &in, &ob) !=
    542           KIT_OK ||
    543       !ob) {
    544     kit_compiler_free(c);
    545     kit_target_free(target);
    546     free(src);
    547     return 1;
    548   }
    549 
    550   memset(&lopts, 0, sizeof lopts);
    551   lopts.output_kind = KIT_LINK_OUTPUT_JIT;
    552   lopts.entry = KIT_SLICE_LIT("test_main");
    553 
    554   memset(&jhost, 0, sizeof jhost);
    555   jhost.execmem = &g_execmem;
    556   lopts.jit_host = &jhost;
    557 
    558   if (!add_runtime_archive(&rt_archive, &rt_data)) {
    559     kit_compiler_free(c);
    560     kit_target_free(target);
    561     free(src);
    562     return 1;
    563   }
    564   if (kit_link_session_new(c, &lopts, &link) != KIT_OK ||
    565       kit_link_session_add_obj(link, ob) != KIT_OK ||
    566       kit_link_session_add_archive_bytes(link, &rt_archive) != KIT_OK ||
    567       kit_link_session_jit(link, &jit) != KIT_OK || !jit) {
    568     kit_link_session_free(link);
    569     kit_compiler_free(c);
    570     kit_target_free(target);
    571     free(rt_data);
    572     free(src);
    573     return 1;
    574   }
    575   kit_link_session_free(link);
    576 
    577   fn = (int (*)(void))kit_jit_lookup(jit, KIT_SLICE_LIT("test_main"));
    578 
    579 #if defined(__aarch64__) || defined(__arm64__)
    580   {
    581     char* td_start = (char*)kit_jit_lookup(jit, KIT_SLICE_LIT("__tdata_start"));
    582     char* td_end = (char*)kit_jit_lookup(jit, KIT_SLICE_LIT("__tdata_end"));
    583     unsigned long bs_n = (unsigned long)(unsigned long long)kit_jit_lookup(
    584         jit, KIT_SLICE_LIT("__tbss_size"));
    585     if (td_start && td_end) {
    586       unsigned long td_n = (unsigned long)(td_end - td_start);
    587       unsigned long i;
    588       for (i = 0; i < td_n; ++i) g_tls_block[16 + i] = td_start[i];
    589       for (i = 0; i < bs_n; ++i) g_tls_block[16 + td_n + i] = 0;
    590     }
    591   }
    592 #endif
    593 
    594   if (fn) {
    595 #if defined(__aarch64__) || defined(__arm64__)
    596     result = call_with_aarch64_tls(fn, g_tls_block);
    597 #else
    598     result = fn();
    599 #endif
    600   } else {
    601     result = 1;
    602   }
    603 
    604   kit_jit_free(jit);
    605   kit_compiler_free(c);
    606   kit_target_free(target);
    607   free(rt_data);
    608   free(src);
    609   return result;
    610 }
    611 
    612 static int usage(void) {
    613   fprintf(stderr,
    614           "usage: parse-runner --emit   FILE.c OUT.o\n"
    615           "       parse-runner --emit-c FILE.c OUT.c\n"
    616           "       parse-runner --jit    FILE.c\n");
    617   return 2;
    618 }
    619 
    620 int main(int argc, char** argv) {
    621   long ps = sysconf(_SC_PAGESIZE);
    622   if (ps > 0) g_execmem.page_size = (size_t)ps;
    623   if (argc < 2) return usage();
    624   if (!strcmp(argv[1], "--emit") && argc == 4)
    625     return mode_emit_impl(argv[2], argv[3], 0);
    626   if (!strcmp(argv[1], "--emit-c") && argc == 4)
    627     return mode_emit_impl(argv[2], argv[3], 1);
    628   if (!strcmp(argv[1], "--jit") && argc == 3) return mode_jit(argv[2]);
    629   return usage();
    630 }