kit

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

interp_program.c (5981B)


      1 /* InterpProgram lifecycle, capture (called from the optimizer), symbol/name
      2  * lookup, and the pluggable memory/symbol resolution used by the engine. */
      3 
      4 #include <string.h>
      5 
      6 #include "core/arena.h"
      7 #include "core/core.h"
      8 #include "core/slice.h"
      9 #include "interp/interp.h"
     10 
     11 static void* ip_alloc(InterpProgram* p, size_t n, size_t align) {
     12   Heap* h = p->c->ctx->heap;
     13   void* q = h->alloc(h, n, align);
     14   if (!q) compiler_panic(p->c, (SrcLoc){0, 0, 0}, "interp: out of memory");
     15   memset(q, 0, n);
     16   return q;
     17 }
     18 
     19 static void ip_free(InterpProgram* p, void* q, size_t n) {
     20   Heap* h = p->c->ctx->heap;
     21   if (q) h->free(h, q, n);
     22 }
     23 
     24 KitInterpProgram* kit_interp_program_new(KitCompiler* cc) {
     25   Compiler* c = (Compiler*)cc;
     26   Heap* h;
     27   InterpProgram* p;
     28   if (!c) return NULL;
     29   h = c->ctx->heap;
     30   p = (InterpProgram*)h->alloc(h, sizeof(*p), _Alignof(InterpProgram));
     31   if (!p) return NULL;
     32   memset(p, 0, sizeof(*p));
     33   p->c = c;
     34   return (KitInterpProgram*)p;
     35 }
     36 
     37 void kit_interp_program_free(KitInterpProgram* pp) {
     38   InterpProgram* p = (InterpProgram*)pp;
     39   Heap* h;
     40   if (!p) return;
     41   h = p->c->ctx->heap;
     42   if (p->funcs) ip_free(p, p->funcs, sizeof(p->funcs[0]) * p->funcs_cap);
     43   /* InterpFunc bodies live on c->tu (compiler-lifetime arena); only the
     44    * function-pointer table and the program struct are heap-owned. */
     45   h->free(h, p, sizeof(*p));
     46 }
     47 
     48 void kit_interp_program_attach(KitInterpProgram* pp, KitCompiler* cc) {
     49   Compiler* c = (Compiler*)cc;
     50   if (!c) return;
     51   c->interp_sink = pp; /* may be NULL to detach */
     52 }
     53 
     54 void kit_interp_program_set_host(KitInterpProgram* pp,
     55                                  const KitInterpHost* host) {
     56   InterpProgram* p = (InterpProgram*)pp;
     57   if (!p) return;
     58   if (host) {
     59     p->host = *host;
     60     p->have_host = 1;
     61   } else {
     62     p->have_host = 0;
     63   }
     64 }
     65 
     66 static void ip_register(InterpProgram* p, InterpFunc* fn) {
     67   if (p->nfuncs == p->funcs_cap) {
     68     u32 ncap = p->funcs_cap ? p->funcs_cap * 2u : 16u;
     69     InterpFunc** nf = ip_alloc(p, sizeof(nf[0]) * ncap, _Alignof(InterpFunc*));
     70     if (p->nfuncs) memcpy(nf, p->funcs, sizeof(nf[0]) * p->nfuncs);
     71     if (p->funcs) ip_free(p, p->funcs, sizeof(p->funcs[0]) * p->funcs_cap);
     72     p->funcs = nf;
     73     p->funcs_cap = ncap;
     74   }
     75   p->funcs[p->nfuncs++] = fn;
     76 }
     77 
     78 void interp_capture_func(void* program, Func* f, ObjSymId sym, const char* name,
     79                          u32 name_len, const ObjBuilder* obj) {
     80   InterpProgram* p = (InterpProgram*)program;
     81   Slice nm;
     82   InterpFunc* fn;
     83   if (!p || !f) return;
     84   nm.s = name;
     85   nm.len = name_len;
     86   fn = interp_lower(p, f, sym, nm, obj);
     87   if (fn) ip_register(p, fn);
     88 }
     89 
     90 InterpFunc* interp_func_for_sym(InterpProgram* p, ObjSymId sym) {
     91   u32 i;
     92   if (!p || sym == OBJ_SYM_NONE) return NULL;
     93   for (i = 0; i < p->nfuncs; ++i)
     94     if (p->funcs[i]->sym == sym) return p->funcs[i];
     95   return NULL;
     96 }
     97 
     98 InterpFunc* interp_func_for_addr(InterpProgram* p, void* addr) {
     99   u32 i;
    100   if (!p || !addr) return NULL;
    101   for (i = 0; i < p->nfuncs; ++i) {
    102     InterpFunc* fn = p->funcs[i];
    103     if (!fn->ok || !fn->name.s) continue;
    104     if (interp_resolve_sym(p, fn->name) == addr) return fn;
    105   }
    106   return NULL;
    107 }
    108 
    109 void* interp_global_base(InterpFunc* fn, ObjSymId sym) {
    110   u32 i;
    111   if (!fn) return NULL;
    112   for (i = 0; i < fn->nglobals; ++i) {
    113     InterpGlobal* g = &fn->globals[i];
    114     if (g->sym != sym) continue;
    115     if (!g->resolved) {
    116       g->cached = interp_resolve_sym(fn->prog, g->name);
    117       g->resolved = 1;
    118     }
    119     return g->cached;
    120   }
    121   return NULL;
    122 }
    123 
    124 void* interp_tls_addr(InterpFunc* fn, ObjSymId sym, i64 addend) {
    125   InterpProgram* p = fn ? fn->prog : NULL;
    126   u32 i;
    127   if (!p) return NULL;
    128   if (p->have_host && p->host.resolve_tls) {
    129     /* Resolve by symbol name (the host maps a thread-local name + addend to the
    130      * running thread's storage, unwrapping a TLV descriptor where needed). */
    131     for (i = 0; i < fn->nglobals; ++i)
    132       if (fn->globals[i].sym == sym)
    133         return p->host.resolve_tls(p->host.ctx, fn->globals[i].name, addend);
    134     return NULL;
    135   }
    136   /* No TLS resolver bound: treat as a plain global (correct only where the
    137    * symbol already denotes the variable's storage). */
    138   {
    139     void* base = interp_global_base(fn, sym);
    140     return base ? (u8*)base + (u64)addend : NULL;
    141   }
    142 }
    143 
    144 /* Match a stored (possibly target-mangled) symbol name against an unmangled
    145  * query, tolerating a single leading-underscore prefix (Mach-O / COFF C
    146  * mangling). ELF stores the bare name, so the exact branch handles it. */
    147 static int name_matches(Slice stored, KitSlice q) {
    148   if (!stored.s) return 0;
    149   if (stored.len == q.len && memcmp(stored.s, q.s, q.len) == 0) return 1;
    150   if (stored.len == q.len + 1u && stored.s[0] == '_' &&
    151       memcmp(stored.s + 1, q.s, q.len) == 0)
    152     return 1;
    153   return 0;
    154 }
    155 
    156 KitInterpFunc* kit_interp_lookup(KitInterpProgram* pp, KitSlice name) {
    157   InterpProgram* p = (InterpProgram*)pp;
    158   u32 i;
    159   if (!p || !name.s) return NULL;
    160   /* Newest-first: the emu re-lifts a block (same guest_pc-derived name) after
    161    * self-modifying code bumps the addr-space generation and flushes the code
    162    * cache. Capture is append-only, so the LAST same-named InterpFunc is the
    163    * freshest re-translation — returning it keeps INTERP in step with the fresh
    164    * code the JIT runs via its per-block object lookup. For the host-identity
    165    * --no-jit path each name is captured exactly once, so last == first. */
    166   for (i = p->nfuncs; i-- > 0;)
    167     if (name_matches(p->funcs[i]->name, name))
    168       return (KitInterpFunc*)p->funcs[i];
    169   return NULL;
    170 }
    171 
    172 u8* interp_translate(InterpProgram* p, u64 addr, u64 n, int perms) {
    173   if (p && p->have_host && p->host.translate)
    174     return p->host.translate(p->host.ctx, addr, n, perms);
    175   /* Host-identity: abstract addresses are real host pointers. */
    176   return (u8*)(uintptr_t)addr;
    177 }
    178 
    179 void* interp_resolve_sym(InterpProgram* p, Slice name) {
    180   if (p && p->have_host && p->host.resolve_sym)
    181     return p->host.resolve_sym(p->host.ctx, name);
    182   return NULL;
    183 }