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 }