File Coverage

lib/XS/JIT/xs_jit.c
Criterion Covered Total %
statement 363 430 84.4
branch 137 246 55.6
condition n/a
subroutine n/a
pod n/a
total 500 676 73.9


line stmt bran cond sub pod time code
1             /*
2             * xs_jit.c - Core C implementation for XS::JIT
3             *
4             * This file provides the JIT compilation functionality that can be
5             * called directly from C (no Perl stack overhead) or via XS bindings.
6             */
7              
8             #include "xs_jit.h"
9             #include
10             #include
11             #include
12             #include
13             #include
14              
15             /* PERL_VERSION_LE/GE macros for compile-time version checks in this file
16             * These are needed because older Perls don't define them */
17             #ifndef PERL_VERSION_DECIMAL
18             # define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
19             #endif
20             #ifndef PERL_DECIMAL_VERSION
21             # define PERL_DECIMAL_VERSION \
22             PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
23             #endif
24             #ifndef PERL_VERSION_GE
25             # define PERL_VERSION_GE(r,v,s) \
26             (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
27             #endif
28             #ifndef PERL_VERSION_LE
29             # define PERL_VERSION_LE(r,v,s) \
30             (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
31             #endif
32              
33             /* Buffer growth size */
34             #define BUFFER_CHUNK 4096
35              
36             /* Maximum path length */
37             #define MAX_PATH_LEN 4096
38              
39             /* Dynamic string buffer */
40             typedef struct {
41             char *data;
42             size_t len;
43             size_t capacity;
44             } StrBuf;
45              
46 276           static void strbuf_init(StrBuf *buf) {
47 276           buf->data = NULL;
48 276           buf->len = 0;
49 276           buf->capacity = 0;
50 276           }
51              
52 0           static void strbuf_free(StrBuf *buf) {
53 0 0         if (buf->data) {
54 0           free(buf->data);
55 0           buf->data = NULL;
56             }
57 0           buf->len = 0;
58 0           buf->capacity = 0;
59 0           }
60              
61 29053           static int strbuf_ensure(StrBuf *buf, size_t needed) {
62 29053 100         if (buf->capacity >= needed) return 1;
63              
64 402 100         size_t new_cap = buf->capacity ? buf->capacity : BUFFER_CHUNK;
65 531 100         while (new_cap < needed) new_cap *= 2;
66              
67 402           char *new_data = realloc(buf->data, new_cap);
68 402 50         if (!new_data) return 0;
69              
70 402           buf->data = new_data;
71 402           buf->capacity = new_cap;
72 402           return 1;
73             }
74              
75 26733           static int strbuf_append(StrBuf *buf, const char *str) {
76 26733           size_t slen = strlen(str);
77 26733 50         if (!strbuf_ensure(buf, buf->len + slen + 1)) return 0;
78 26733           memcpy(buf->data + buf->len, str, slen + 1);
79 26733           buf->len += slen;
80 26733           return 1;
81             }
82              
83 2320           static int strbuf_appendf(StrBuf *buf, const char *fmt, ...) {
84             va_list args, args2;
85 2320           va_start(args, fmt);
86 2320           va_copy(args2, args);
87              
88 2320           int needed = vsnprintf(NULL, 0, fmt, args);
89 2320           va_end(args);
90              
91 2320 50         if (needed < 0) {
92 0           va_end(args2);
93 0           return 0;
94             }
95              
96 2320 50         if (!strbuf_ensure(buf, buf->len + needed + 1)) {
97 0           va_end(args2);
98 0           return 0;
99             }
100              
101 2320           vsnprintf(buf->data + buf->len, needed + 1, fmt, args2);
102 2320           va_end(args2);
103 2320           buf->len += needed;
104 2320           return 1;
105             }
106              
107             /* Convert module name to safe C identifier */
108 1920           static void safe_name(const char *name, char *out, size_t outlen) {
109 1920           size_t i, j = 0;
110 35688 100         for (i = 0; name[i] && j < outlen - 1; i++) {
    50          
111 33768           char c = name[i];
112 33768 100         if (c == ':') {
113 1941           out[j++] = '_';
114 1941 50         if (name[i+1] == ':') i++;
115 31827 100         } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
    50          
    100          
    100          
    50          
116 1559 100         (c >= '0' && c <= '9') || c == '_') {
    50          
117 31827           out[j++] = c;
118             } else {
119 0           out[j++] = '_';
120             }
121             }
122 1920           out[j] = '\0';
123 1920           }
124              
125             /* Simple MD5-like hash for cache key (uses Perl's Digest::MD5) */
126 11           static char* compute_cache_key(pTHX_ const char *code, const char *name) {
127 11           dSP;
128             SV *input;
129 11           char *result = NULL;
130              
131             /* Build input string: code + name + archname + perl version */
132 11           HV *config = get_hv("Config::Config", 0);
133 11 50         SV **archname_sv = config ? hv_fetch(config, "archname", 8, 0) : NULL;
134 11 50         const char *archname = (archname_sv && *archname_sv) ? SvPV_nolen(*archname_sv) : "unknown";
    50          
135              
136 11           input = newSVpvf("%s\n%s\n%s\n%s", code, name, archname, PERL_VERSION_STRING);
137              
138 11           ENTER;
139 11           SAVETMPS;
140              
141             /* require Digest::MD5 */
142 11           eval_pv("require Digest::MD5", G_DISCARD);
143 11 50         if (SvTRUE(ERRSV)) {
    50          
144 0           SvREFCNT_dec(input);
145 0 0         FREETMPS;
146 0           LEAVE;
147 0           return NULL;
148             }
149              
150 11 50         PUSHMARK(SP);
151 11 50         XPUSHs(input);
152 11           PUTBACK;
153              
154 11           int count = call_pv("Digest::MD5::md5_hex", G_SCALAR);
155 11           SPAGAIN;
156              
157 11 50         if (count == 1) {
158 11           SV *md5_sv = POPs;
159             STRLEN len;
160 11           const char *md5 = SvPV(md5_sv, len);
161 11           result = (char*)malloc(len + 1);
162 11 50         if (result) {
163 11           memcpy(result, md5, len);
164 11           result[len] = '\0';
165             }
166             }
167              
168 11           PUTBACK;
169 11 50         FREETMPS;
170 11           LEAVE;
171              
172 11           SvREFCNT_dec(input);
173 11           return result;
174             }
175              
176             /* Get cache directory path */
177 286           static const char* get_cache_dir(const char *cache_dir) {
178 286 50         return cache_dir ? cache_dir : "_CACHED_XS";
179             }
180              
181             /* Build path to cached .so file */
182 11           int xs_jit_cache_path(pTHX_ const char *code, const char *name,
183             const char *cache_dir, char *out_path, size_t out_len) {
184 11           char *key = compute_cache_key(aTHX_ code, name);
185 11 50         if (!key) return 0;
186              
187 11           HV *config = get_hv("Config::Config", 0);
188 11 50         SV **dlext_sv = config ? hv_fetch(config, "dlext", 5, 0) : NULL;
189 11 50         const char *dlext = (dlext_sv && *dlext_sv) ? SvPV_nolen(*dlext_sv) : "so";
    50          
190              
191 11           const char *dir = get_cache_dir(cache_dir);
192              
193             char safe[256];
194 11           safe_name(name, safe, sizeof(safe));
195              
196 11           snprintf(out_path, out_len, "%s/lib/auto/%s/%s.%s",
197             dir, safe, safe, dlext);
198              
199 11           free(key);
200 11           return 1;
201             }
202              
203             /* Check if cached version exists */
204 11           int xs_jit_is_cached(pTHX_ const char *code, const char *name,
205             const char *cache_dir) {
206             char path[MAX_PATH_LEN];
207 11 50         if (!xs_jit_cache_path(aTHX_ code, name, cache_dir, path, sizeof(path))) {
208 0           return 0;
209             }
210              
211             Stat_t st;
212 11           return PerlLIO_stat(path, &st) == 0;
213             }
214              
215             /* Generate XS wrapper/alias for a single function */
216 542           static int generate_wrapper(StrBuf *buf, const char *target,
217             const char *source, int has_varargs, int is_xs_native) {
218             char safe_target[256];
219 542           safe_name(target, safe_target, sizeof(safe_target));
220              
221 542 100         if (is_xs_native) {
222             /* For XS-native functions, just create an alias - no wrapper needed.
223             * The user function already has proper XS signature and handles
224             * dXSARGS, ST(), XSRETURN, etc. itself.
225             */
226 471           strbuf_appendf(buf, "\n/* Alias for XS-native %s -> %s */\n", source, target);
227 471           strbuf_appendf(buf, "#define XS_%s %s\n", safe_target, source);
228             } else {
229 71           strbuf_appendf(buf, "\n/* XS wrapper for %s -> %s */\n", source, target);
230 71           strbuf_appendf(buf, "XS_EUPXS(XS_%s) {\n", safe_target);
231 71           strbuf_append(buf, " dVAR; dXSARGS;\n");
232              
233 71 50         if (has_varargs) {
234 71           strbuf_append(buf, " if (items < 1)\n");
235 71           strbuf_appendf(buf, " croak_xs_usage(cv, \"self, ...\");\n");
236             }
237              
238 71           strbuf_append(buf, " {\n");
239 71           strbuf_append(buf, " SV *RETVAL;\n");
240 71           strbuf_append(buf, " I32* temp = PL_markstack_ptr++;\n");
241 71           strbuf_appendf(buf, " RETVAL = %s(ST(0));\n", source);
242 71           strbuf_append(buf, " PL_markstack_ptr = temp;\n");
243 71           strbuf_append(buf, " if (RETVAL) {\n");
244 71           strbuf_append(buf, " RETVAL = sv_2mortal(RETVAL);\n");
245 71           strbuf_append(buf, " } else {\n");
246 71           strbuf_append(buf, " RETVAL = &PL_sv_undef;\n");
247 71           strbuf_append(buf, " }\n");
248 71           strbuf_append(buf, " ST(0) = RETVAL;\n");
249 71           strbuf_append(buf, " }\n");
250 71           strbuf_append(buf, " XSRETURN(1);\n");
251 71           strbuf_append(buf, "}\n");
252             }
253              
254 542           return 1;
255             }
256              
257             /* Generate boot function */
258 276           static int generate_boot(StrBuf *buf, const char *module_name,
259             XS_JIT_Func *functions, int num_funcs) {
260             char safe_module[256];
261             int i;
262 276           safe_name(module_name, safe_module, sizeof(safe_module));
263              
264 276           strbuf_append(buf, "\n/* Boot function */\n");
265 276           strbuf_append(buf, "#ifdef __cplusplus\n");
266 276           strbuf_append(buf, "extern \"C\" {\n");
267 276           strbuf_append(buf, "#endif\n");
268 276           strbuf_appendf(buf, "XS_EXTERNAL(boot_%s);\n", safe_module);
269 276           strbuf_appendf(buf, "XS_EXTERNAL(boot_%s) {\n", safe_module);
270              
271             /* JIT: emit correct boot args for this Perl version */
272             #if PERL_VERSION_LE(5, 21, 5)
273             strbuf_append(buf, " dVAR; dXSARGS;\n");
274             #else
275 276           strbuf_append(buf, " dVAR; dXSBOOTARGSXSAPIVERCHK;\n");
276             #endif
277              
278             /* JIT: emit correct file declaration for this Perl version */
279             #if PERL_VERSION_LE(5, 8, 999)
280             strbuf_append(buf, " char* file = __FILE__;\n");
281             #else
282 276           strbuf_append(buf, " const char* file = __FILE__;\n");
283             #endif
284              
285 276           strbuf_append(buf, "\n");
286 276           strbuf_append(buf, " PERL_UNUSED_VAR(file);\n");
287 276           strbuf_append(buf, " PERL_UNUSED_VAR(cv);\n");
288 276           strbuf_append(buf, " PERL_UNUSED_VAR(items);\n");
289 276           strbuf_append(buf, "\n");
290              
291             /* JIT: emit version check only for older Perls */
292             #if PERL_VERSION_LE(5, 21, 5)
293             strbuf_append(buf, " XS_VERSION_BOOTCHECK;\n");
294             # ifdef XS_APIVERSION_BOOTCHECK
295             strbuf_append(buf, " XS_APIVERSION_BOOTCHECK;\n");
296             # endif
297             #endif
298 276           strbuf_append(buf, "\n");
299              
300             /* Register each function */
301 818 100         for (i = 0; i < num_funcs; i++) {
302 542 50         if (!functions[i].target) break;
303              
304             char safe_target[256];
305 542           safe_name(functions[i].target, safe_target, sizeof(safe_target));
306              
307 542           strbuf_appendf(buf, " newXS_deffile(\"%s\", XS_%s);\n",
308 542           functions[i].target, safe_target);
309             }
310              
311 276           strbuf_append(buf, "\n");
312              
313             /* JIT: emit correct boot epilog for this Perl version */
314             #if PERL_VERSION_LE(5, 21, 5)
315             # if PERL_VERSION_GE(5, 9, 0)
316             strbuf_append(buf, " if (PL_unitcheckav)\n");
317             strbuf_append(buf, " call_list(PL_scopestack_ix, PL_unitcheckav);\n");
318             # endif
319             strbuf_append(buf, " XSRETURN_YES;\n");
320             #else
321 276           strbuf_append(buf, " Perl_xs_boot_epilog(aTHX_ ax);\n");
322             #endif
323              
324 276           strbuf_append(buf, "}\n");
325 276           strbuf_append(buf, "#ifdef __cplusplus\n");
326 276           strbuf_append(buf, "}\n");
327 276           strbuf_append(buf, "#endif\n");
328              
329 276           return 1;
330             }
331              
332             /* Generate complete C code */
333 276           char* xs_jit_generate_code(pTHX_ const char *user_code,
334             const char *module_name,
335             XS_JIT_Func *functions,
336             int num_funcs) {
337             StrBuf buf;
338             int i;
339 276           strbuf_init(&buf);
340              
341             /* Standard headers */
342 276           strbuf_append(&buf, "/*\n");
343 276           strbuf_append(&buf, " * Generated by XS::JIT\n");
344 276           strbuf_append(&buf, " * Do not edit this file directly.\n");
345 276           strbuf_append(&buf, " */\n\n");
346              
347 276           strbuf_append(&buf, "#include \"EXTERN.h\"\n");
348 276           strbuf_append(&buf, "#include \"perl.h\"\n");
349 276           strbuf_append(&buf, "#include \"XSUB.h\"\n");
350 276           strbuf_append(&buf, "\n");
351              
352             /* PERL_STATIC_INLINE compatibility for Perl < 5.13.4 */
353 276           strbuf_append(&buf, "#ifndef PERL_STATIC_INLINE\n");
354 276           strbuf_append(&buf, "# define PERL_STATIC_INLINE static\n");
355 276           strbuf_append(&buf, "#endif\n");
356 276           strbuf_append(&buf, "\n");
357              
358             /* PERL_VERSION_GE macro for version checks */
359 276           strbuf_append(&buf, "#ifndef PERL_VERSION_GE\n");
360 276           strbuf_append(&buf, "# define PERL_VERSION_GE(r,v,s) \\\n");
361 276           strbuf_append(&buf, " (PERL_REVISION > (r) || (PERL_REVISION == (r) && \\\n");
362 276           strbuf_append(&buf, " (PERL_VERSION > (v) || (PERL_VERSION == (v) && PERL_SUBVERSION >= (s)))))\n");
363 276           strbuf_append(&buf, "#endif\n");
364 276           strbuf_append(&buf, "\n");
365              
366             /* XOP API backwards compatibility for pre-5.14 Perl */
367 276           strbuf_append(&buf, "#if !PERL_VERSION_GE(5,14,0)\n");
368 276           strbuf_append(&buf, "/* Pre-5.14: use deprecated custom op interface */\n");
369 276           strbuf_append(&buf, "# ifndef XOP_DEFINED_BY_COMPAT\n");
370 276           strbuf_append(&buf, "# define XOP_DEFINED_BY_COMPAT 1\n");
371 276           strbuf_append(&buf, "typedef struct { const char *xop_name; const char *xop_desc; U32 xop_class; } XOP;\n");
372 276           strbuf_append(&buf, "# endif\n");
373 276           strbuf_append(&buf, "# ifndef OA_UNOP\n");
374 276           strbuf_append(&buf, "# define OA_UNOP 1\n");
375 276           strbuf_append(&buf, "# endif\n");
376 276           strbuf_append(&buf, "# ifndef OA_BINOP\n");
377 276           strbuf_append(&buf, "# define OA_BINOP 2\n");
378 276           strbuf_append(&buf, "# endif\n");
379 276           strbuf_append(&buf, "# ifndef XopENTRY_set\n");
380 276           strbuf_append(&buf, "# define XopENTRY_set(xop, field, value) do { (xop)->field = (value); } while(0)\n");
381 276           strbuf_append(&buf, "# endif\n");
382 276           strbuf_append(&buf, "PERL_STATIC_INLINE void S_xop_compat_register(pTHX_ Perl_ppaddr_t ppfunc, const char *name, const char *desc) {\n");
383 276           strbuf_append(&buf, " if (!PL_custom_op_names) PL_custom_op_names = newHV();\n");
384 276           strbuf_append(&buf, " if (!PL_custom_op_descs) PL_custom_op_descs = newHV();\n");
385 276           strbuf_append(&buf, " hv_store(PL_custom_op_names, (char*)&ppfunc, sizeof(ppfunc), newSVpv(name, 0), 0);\n");
386 276           strbuf_append(&buf, " hv_store(PL_custom_op_descs, (char*)&ppfunc, sizeof(ppfunc), newSVpv(desc, 0), 0);\n");
387 276           strbuf_append(&buf, "}\n");
388 276           strbuf_append(&buf, "# undef Perl_custom_op_register\n");
389 276           strbuf_append(&buf, "# ifdef PERL_IMPLICIT_CONTEXT\n");
390 276           strbuf_append(&buf, "# define Perl_custom_op_register(ctx, ppfunc, xop) \\\n");
391 276           strbuf_append(&buf, " S_xop_compat_register((ctx), (Perl_ppaddr_t)(ppfunc), (xop)->xop_name, (xop)->xop_desc)\n");
392 276           strbuf_append(&buf, "# else\n");
393 276           strbuf_append(&buf, "# define Perl_custom_op_register(ppfunc, xop) \\\n");
394 276           strbuf_append(&buf, " S_xop_compat_register(aTHX_ (Perl_ppaddr_t)(ppfunc), (xop)->xop_name, (xop)->xop_desc)\n");
395 276           strbuf_append(&buf, "# endif\n");
396 276           strbuf_append(&buf, "#endif /* !PERL_VERSION_GE(5,14,0) */\n");
397 276           strbuf_append(&buf, "\n");
398              
399             /* Version compatibility macros - only emit if not defined on build system */
400             #ifndef PERL_UNUSED_VAR
401             strbuf_append(&buf, "#ifndef PERL_UNUSED_VAR\n");
402             strbuf_append(&buf, "# define PERL_UNUSED_VAR(var) if (0) var = var\n");
403             strbuf_append(&buf, "#endif\n");
404             strbuf_append(&buf, "\n");
405             #endif
406             #ifndef dVAR
407             strbuf_append(&buf, "#ifndef dVAR\n");
408             strbuf_append(&buf, "# define dVAR dNOOP\n");
409             strbuf_append(&buf, "#endif\n");
410             strbuf_append(&buf, "\n");
411             #endif
412              
413             /* PERL_VERSION macros - only emit if not defined on build system */
414             #ifndef PERL_VERSION_DECIMAL
415             strbuf_append(&buf, "#ifndef PERL_VERSION_DECIMAL\n");
416             strbuf_append(&buf, "# define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)\n");
417             strbuf_append(&buf, "#endif\n");
418             strbuf_append(&buf, "#ifndef PERL_DECIMAL_VERSION\n");
419             strbuf_append(&buf, "# define PERL_DECIMAL_VERSION \\\n");
420             strbuf_append(&buf, " PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)\n");
421             strbuf_append(&buf, "#endif\n");
422             strbuf_append(&buf, "#ifndef PERL_VERSION_GE\n");
423             strbuf_append(&buf, "# define PERL_VERSION_GE(r,v,s) \\\n");
424             strbuf_append(&buf, " (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))\n");
425             strbuf_append(&buf, "#endif\n");
426             strbuf_append(&buf, "#ifndef PERL_VERSION_LE\n");
427             strbuf_append(&buf, "# define PERL_VERSION_LE(r,v,s) \\\n");
428             strbuf_append(&buf, " (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))\n");
429             strbuf_append(&buf, "#endif\n");
430             strbuf_append(&buf, "\n");
431             #endif
432              
433             /* XS macros - only emit if not defined on build system */
434             #ifndef XS_EXTERNAL
435             strbuf_append(&buf, "#ifndef XS_EXTERNAL\n");
436             strbuf_append(&buf, "# define XS_EXTERNAL(name) XS(name)\n");
437             strbuf_append(&buf, "#endif\n");
438             strbuf_append(&buf, "#ifndef XS_INTERNAL\n");
439             strbuf_append(&buf, "# define XS_INTERNAL(name) XS(name)\n");
440             strbuf_append(&buf, "#endif\n");
441             #endif
442 276           strbuf_append(&buf, "#undef XS_EUPXS\n");
443 276           strbuf_append(&buf, "#if defined(PERL_EUPXS_ALWAYS_EXPORT)\n");
444 276           strbuf_append(&buf, "# define XS_EUPXS(name) XS_EXTERNAL(name)\n");
445 276           strbuf_append(&buf, "#else\n");
446 276           strbuf_append(&buf, "# define XS_EUPXS(name) XS_INTERNAL(name)\n");
447 276           strbuf_append(&buf, "#endif\n");
448 276           strbuf_append(&buf, "\n");
449              
450             /* newXS_deffile compatibility - JIT: emit only correct version */
451             #if PERL_VERSION_LE(5, 21, 5)
452             strbuf_append(&buf, "#define newXS_deffile(a,b) Perl_newXS(aTHX_ a,b,file)\n");
453             #else
454 276           strbuf_append(&buf, "#define newXS_deffile(a,b) Perl_newXS_deffile(aTHX_ a,b)\n");
455             #endif
456 276           strbuf_append(&buf, "\n");
457              
458             /* Op sibling compatibility for Perl < 5.22 (added in 5.21.2)
459             * Only emit compat code when XS::JIT is built on older Perl - true JIT behavior */
460             #ifndef OpHAS_SIBLING
461             strbuf_append(&buf, "/* Op sibling compatibility for Perl < 5.22 */\n");
462             strbuf_append(&buf, "#ifndef OpHAS_SIBLING\n");
463             strbuf_append(&buf, "# define OpHAS_SIBLING(o) ((o)->op_sibling != NULL)\n");
464             strbuf_append(&buf, "# define OpSIBLING(o) ((o)->op_sibling)\n");
465             strbuf_append(&buf, "# define OpMORESIB_set(o, sib) ((o)->op_sibling = (sib))\n");
466             strbuf_append(&buf, "# define OpLASTSIB_set(o, parent) ((o)->op_sibling = NULL)\n");
467             strbuf_append(&buf, "PERL_STATIC_INLINE OP*\n");
468             strbuf_append(&buf, "S_op_sibling_splice_compat(OP *parent, OP *start, int del_count, OP *insert)\n");
469             strbuf_append(&buf, "{\n");
470             strbuf_append(&buf, " OP *first, *rest, *last_del = NULL;\n");
471             strbuf_append(&buf, " if (!parent) return NULL;\n");
472             strbuf_append(&buf, " if (start) first = start->op_sibling;\n");
473             strbuf_append(&buf, " else first = cLISTOPx(parent)->op_first;\n");
474             strbuf_append(&buf, " rest = first;\n");
475             strbuf_append(&buf, " while (del_count && rest) {\n");
476             strbuf_append(&buf, " last_del = rest;\n");
477             strbuf_append(&buf, " rest = rest->op_sibling;\n");
478             strbuf_append(&buf, " del_count--;\n");
479             strbuf_append(&buf, " }\n");
480             strbuf_append(&buf, " if (last_del) last_del->op_sibling = NULL;\n");
481             strbuf_append(&buf, " if (insert) {\n");
482             strbuf_append(&buf, " OP *insert_last = insert;\n");
483             strbuf_append(&buf, " while (insert_last->op_sibling) insert_last = insert_last->op_sibling;\n");
484             strbuf_append(&buf, " insert_last->op_sibling = rest;\n");
485             strbuf_append(&buf, " if (start) start->op_sibling = insert;\n");
486             strbuf_append(&buf, " else cLISTOPx(parent)->op_first = insert;\n");
487             strbuf_append(&buf, " } else {\n");
488             strbuf_append(&buf, " if (start) start->op_sibling = rest;\n");
489             strbuf_append(&buf, " else cLISTOPx(parent)->op_first = rest;\n");
490             strbuf_append(&buf, " }\n");
491             strbuf_append(&buf, " return first;\n");
492             strbuf_append(&buf, "}\n");
493             strbuf_append(&buf, "# define op_sibling_splice(parent, start, del_count, insert) \\\n");
494             strbuf_append(&buf, " S_op_sibling_splice_compat(parent, start, del_count, insert)\n");
495             strbuf_append(&buf, "#endif\n");
496             strbuf_append(&buf, "\n");
497             #endif
498              
499             /* XS::JIT convenience macros */
500 276           strbuf_append(&buf, "/* XS::JIT convenience macros */\n");
501 276           strbuf_append(&buf, "#define JIT_ARGS dTHX; dXSARGS\n");
502 276           strbuf_append(&buf, "\n");
503              
504             /* Inline compatibility macros */
505 276           strbuf_append(&buf, "/* Inline::C compatibility macros */\n");
506 276           strbuf_append(&buf, "#define Inline_Stack_Vars dXSARGS\n");
507 276           strbuf_append(&buf, "#define Inline_Stack_Items items\n");
508 276           strbuf_append(&buf, "#define Inline_Stack_Item(x) ST(x)\n");
509 276           strbuf_append(&buf, "#define Inline_Stack_Reset sp = mark\n");
510 276           strbuf_append(&buf, "#define Inline_Stack_Push(x) XPUSHs(x)\n");
511 276           strbuf_append(&buf, "#define Inline_Stack_Done PUTBACK\n");
512 276           strbuf_append(&buf, "#define Inline_Stack_Return(x) XSRETURN(x)\n");
513 276           strbuf_append(&buf, "#define Inline_Stack_Void XSRETURN(0)\n");
514 276           strbuf_append(&buf, "\n");
515              
516             /* User code */
517 276           strbuf_append(&buf, "/* ========== User Code ========== */\n\n");
518 276           strbuf_append(&buf, user_code);
519 276           strbuf_append(&buf, "\n\n");
520 276           strbuf_append(&buf, "/* ========== XS Wrappers ========== */\n");
521              
522             /* Generate wrapper for each function */
523 818 100         for (i = 0; i < num_funcs; i++) {
524 542 50         if (!functions[i].target) break;
525 542           generate_wrapper(&buf, functions[i].target, functions[i].source,
526 542           functions[i].has_varargs, functions[i].is_xs_native);
527             }
528              
529             /* Generate boot function */
530 276           generate_boot(&buf, module_name, functions, num_funcs);
531              
532 276           return buf.data; /* Caller must free */
533             }
534              
535             /* Create directory recursively */
536 275           static int mkdir_p(const char *path) {
537             char tmp[MAX_PATH_LEN];
538 275           char *p = NULL;
539             size_t len;
540              
541 275           snprintf(tmp, sizeof(tmp), "%s", path);
542 275           len = strlen(tmp);
543 275 50         if (tmp[len - 1] == '/') tmp[len - 1] = 0;
544              
545 10807 100         for (p = tmp + 1; *p; p++) {
546 10532 100         if (*p == '/') {
547 1069           *p = 0;
548 1069           mkdir(tmp, 0755);
549 1069           *p = '/';
550             }
551             }
552 275 100         return mkdir(tmp, 0755) == 0 || errno == EEXIST;
    50          
553             }
554              
555             /* Compile C file to shared object */
556 275           int xs_jit_compile_file(pTHX_ const char *c_file, const char *so_file,
557             const char *extra_cflags, const char *extra_ldflags) {
558 275           HV *config = get_hv("Config::Config", 0);
559 275 50         if (!config) {
560 0           warn("XS::JIT: Cannot access %%Config");
561 0           return 0;
562             }
563              
564 275           SV **cc_sv = hv_fetch(config, "cc", 2, 0);
565 275           SV **ccflags_sv = hv_fetch(config, "ccflags", 7, 0);
566 275           SV **optimize_sv = hv_fetch(config, "optimize", 8, 0);
567 275           SV **cccdlflags_sv = hv_fetch(config, "cccdlflags", 10, 0);
568 275           SV **lddlflags_sv = hv_fetch(config, "lddlflags", 9, 0);
569 275           SV **archlib_sv = hv_fetch(config, "archlib", 7, 0);
570             #ifdef WIN32
571             SV **libperl_sv = hv_fetch(config, "libperl", 7, 0);
572             #endif
573              
574 275 50         const char *cc = (cc_sv && *cc_sv) ? SvPV_nolen(*cc_sv) : "cc";
    50          
575 275 50         const char *ccflags = (ccflags_sv && *ccflags_sv) ? SvPV_nolen(*ccflags_sv) : "";
    50          
576 275 50         const char *optimize = (optimize_sv && *optimize_sv) ? SvPV_nolen(*optimize_sv) : "-O2";
    50          
577 275 50         const char *cccdlflags = (cccdlflags_sv && *cccdlflags_sv) ? SvPV_nolen(*cccdlflags_sv) : "";
    50          
578 275 50         const char *lddlflags = (lddlflags_sv && *lddlflags_sv) ? SvPV_nolen(*lddlflags_sv) : "";
    50          
579 275 50         const char *archlib = (archlib_sv && *archlib_sv) ? SvPV_nolen(*archlib_sv) : "";
    50          
580              
581             /* Default to empty string if NULL */
582 275 50         if (!extra_cflags) extra_cflags = "";
583 275 50         if (!extra_ldflags) extra_ldflags = "";
584              
585             char o_file[MAX_PATH_LEN];
586 275           snprintf(o_file, sizeof(o_file), "%s.o", c_file);
587              
588             char cmd[MAX_PATH_LEN * 4]; /* Larger buffer for extra flags */
589             int ret;
590              
591             /* On macOS, Perl headers live inside the SDK, so use -iwithsysroot
592             (matching what ExtUtils::MakeMaker generates) instead of plain -I */
593             #ifdef __APPLE__
594             const char *inc_flag = "-iwithsysroot";
595             #else
596 275           const char *inc_flag = "-I";
597             #endif
598              
599             /* Ensure C99 mode for for-loop variable declarations; skip if ccflags already sets -std= */
600 275 50         const char *std_flag = (strstr(ccflags, "-std=") == NULL) ? "-std=gnu99" : "";
601              
602             /* Compile to object with optimization and extra cflags */
603 275           snprintf(cmd, sizeof(cmd), "%s %s %s %s %s %s -c -o \"%s\" %s \"%s/CORE\" \"%s\" 2>&1",
604             cc, ccflags, std_flag, optimize, cccdlflags, extra_cflags, o_file, inc_flag, archlib, c_file);
605              
606 275           ret = system(cmd);
607 275 100         if (ret != 0) {
608 1           warn("XS::JIT: Compilation failed: %s", cmd);
609 1           return 0;
610             }
611              
612             /* Link to shared object with extra ldflags */
613             #ifdef WIN32
614             /* On Win32, DLLs must resolve all symbols at link time.
615             Derive -lperl5XX from $Config{libperl} (e.g. "libperl542.a" -> "-lperl542") */
616             char perl_lib_flag[64] = "";
617             if (libperl_sv && *libperl_sv) {
618             const char *libperl = SvPV_nolen(*libperl_sv);
619             /* Strip leading "lib" and trailing ".a" to get e.g. "perl542" */
620             if (strncmp(libperl, "lib", 3) == 0) {
621             const char *start = libperl + 3;
622             const char *dot = strrchr(start, '.');
623             size_t len = dot ? (size_t)(dot - start) : strlen(start);
624             if (len < sizeof(perl_lib_flag) - 3) {
625             snprintf(perl_lib_flag, sizeof(perl_lib_flag), "-l%.*s", (int)len, start);
626             }
627             }
628             }
629             snprintf(cmd, sizeof(cmd), "%s %s %s -o \"%s\" \"%s\" %s 2>&1",
630             cc, lddlflags, extra_ldflags, so_file, o_file, perl_lib_flag);
631             #else
632 274           snprintf(cmd, sizeof(cmd), "%s %s %s -o \"%s\" \"%s\" 2>&1",
633             cc, lddlflags, extra_ldflags, so_file, o_file);
634             #endif
635              
636 274           ret = system(cmd);
637 274 50         if (ret != 0) {
638 0           warn("XS::JIT: Linking failed: %s", cmd);
639 0           return 0;
640             }
641              
642             /* Clean up object file */
643 274           unlink(o_file);
644              
645 274           return 1;
646             }
647              
648             /* Load compiled module using DynaLoader */
649 274           int xs_jit_load(pTHX_ const char *module_name, const char *so_file) {
650 274           dSP;
651             char safe_module[256];
652 274           safe_name(module_name, safe_module, sizeof(safe_module));
653              
654             /* Resolve to absolute path - macOS hardened runtime rejects relative paths in dlopen */
655             char abs_so_file[MAX_PATH_LEN];
656             #ifdef WIN32
657             if (_fullpath(abs_so_file, so_file, MAX_PATH_LEN)) {
658             so_file = abs_so_file;
659             }
660             #else
661 274 100         if (so_file[0] != '/') {
662 30 50         if (realpath(so_file, abs_so_file)) {
663 30           so_file = abs_so_file;
664             }
665             }
666             #endif
667              
668 274           ENTER;
669 274           SAVETMPS;
670              
671             /* require DynaLoader */
672 274           eval_pv("require DynaLoader", G_DISCARD);
673 274 50         if (SvTRUE(ERRSV)) {
    50          
674 0 0         warn("XS::JIT: Cannot load DynaLoader: %s", SvPV_nolen(ERRSV));
675 0 0         FREETMPS;
676 0           LEAVE;
677 0           return 0;
678             }
679              
680             /* dl_load_file */
681 274 50         PUSHMARK(SP);
682 274 50         XPUSHs(sv_2mortal(newSVpv(so_file, 0)));
683 274 50         XPUSHs(sv_2mortal(newSViv(0))); /* flags */
684 274           PUTBACK;
685              
686 274           int count = call_pv("DynaLoader::dl_load_file", G_SCALAR);
687 274           SPAGAIN;
688              
689 274           SV *libref_sv = NULL;
690 274 50         if (count == 1) {
691 274           libref_sv = POPs;
692 274           SvREFCNT_inc(libref_sv);
693             }
694 274           PUTBACK;
695              
696 274 50         if (!libref_sv || !SvOK(libref_sv)) {
    50          
697             /* Get error */
698 0 0         PUSHMARK(SP);
699 0           PUTBACK;
700 0           call_pv("DynaLoader::dl_error", G_SCALAR);
701 0           SPAGAIN;
702 0           SV *err = POPs;
703 0           warn("XS::JIT: dl_load_file failed: %s", SvPV_nolen(err));
704 0           PUTBACK;
705 0 0         FREETMPS;
706 0           LEAVE;
707 0           return 0;
708             }
709              
710             /* dl_find_symbol for boot function */
711             char boot_name[300];
712 274           snprintf(boot_name, sizeof(boot_name), "boot_%s", safe_module);
713              
714 274 50         PUSHMARK(SP);
715 274 50         XPUSHs(libref_sv);
716 274 50         XPUSHs(sv_2mortal(newSVpv(boot_name, 0)));
717 274           PUTBACK;
718              
719 274           count = call_pv("DynaLoader::dl_find_symbol", G_SCALAR);
720 274           SPAGAIN;
721              
722 274           SV *symref_sv = NULL;
723 274 50         if (count == 1) {
724 274           symref_sv = POPs;
725 274           SvREFCNT_inc(symref_sv);
726             }
727 274           PUTBACK;
728              
729 274 50         if (!symref_sv || !SvOK(symref_sv)) {
    50          
730 0 0         PUSHMARK(SP);
731 0           PUTBACK;
732 0           call_pv("DynaLoader::dl_error", G_SCALAR);
733 0           SPAGAIN;
734 0           SV *err = POPs;
735 0           warn("XS::JIT: dl_find_symbol failed for %s: %s", boot_name, SvPV_nolen(err));
736 0           PUTBACK;
737 0           SvREFCNT_dec(libref_sv);
738 0 0         FREETMPS;
739 0           LEAVE;
740 0           return 0;
741             }
742              
743             /* dl_install_xsub */
744             char bootstrap_name[300];
745 274           snprintf(bootstrap_name, sizeof(bootstrap_name), "%s::bootstrap", module_name);
746              
747 274 50         PUSHMARK(SP);
748 274 50         XPUSHs(sv_2mortal(newSVpv(bootstrap_name, 0)));
749 274 50         XPUSHs(symref_sv);
750 274 50         XPUSHs(sv_2mortal(newSVpv(so_file, 0)));
751 274           PUTBACK;
752              
753 274           count = call_pv("DynaLoader::dl_install_xsub", G_SCALAR);
754 274           SPAGAIN;
755              
756 274           SV *xs_sv = NULL;
757 274 50         if (count == 1) {
758 274           xs_sv = POPs;
759             }
760 274           PUTBACK;
761              
762 274 50         if (!xs_sv || !SvOK(xs_sv)) {
    50          
763 0           warn("XS::JIT: dl_install_xsub failed for %s", bootstrap_name);
764 0           SvREFCNT_dec(libref_sv);
765 0           SvREFCNT_dec(symref_sv);
766 0 0         FREETMPS;
767 0           LEAVE;
768 0           return 0;
769             }
770              
771             /* Call the boot function */
772 274 50         PUSHMARK(SP);
773 274 50         XPUSHs(sv_2mortal(newSVpv(module_name, 0)));
774 274           PUTBACK;
775              
776 274           call_sv(xs_sv, G_DISCARD | G_EVAL);
777              
778 274 50         if (SvTRUE(ERRSV)) {
    50          
779 0 0         warn("XS::JIT: boot function failed: %s", SvPV_nolen(ERRSV));
780 0           SvREFCNT_dec(libref_sv);
781 0           SvREFCNT_dec(symref_sv);
782 0 0         FREETMPS;
783 0           LEAVE;
784 0           return 0;
785             }
786              
787 274           SvREFCNT_dec(libref_sv);
788 274           SvREFCNT_dec(symref_sv);
789 274 50         FREETMPS;
790 274           LEAVE;
791              
792 274           return 1;
793             }
794              
795             /* Check if any of the target functions are already defined */
796 277           static int functions_already_loaded(pTHX_ XS_JIT_Func *functions, int num_functions) {
797             int i;
798 817 100         for (i = 0; i < num_functions && functions[i].target; i++) {
    50          
799             /* Split target into package and function name */
800 543           const char *target = functions[i].target;
801 543           const char *last_colon = strrchr(target, ':');
802 543 50         if (last_colon && last_colon > target && *(last_colon - 1) == ':') {
    50          
    50          
803             /* Extract package name */
804 543           size_t pkg_len = last_colon - target - 1;
805             char pkg[256];
806 543 50         if (pkg_len >= sizeof(pkg)) pkg_len = sizeof(pkg) - 1;
807 543           strncpy(pkg, target, pkg_len);
808 543           pkg[pkg_len] = '\0';
809              
810             /* Check if function exists in the stash */
811 543           HV *stash = gv_stashpv(pkg, 0);
812 543 100         if (stash) {
813 194           const char *func_name = last_colon + 1;
814 194           GV *gv = (GV*)hv_fetch(stash, func_name, strlen(func_name), 0);
815 194 100         if (gv && *(SV**)gv && GvCV(*(GV**)gv)) {
    50          
    100          
816             /* Function already exists */
817 3           return 1;
818             }
819             }
820             }
821             }
822 274           return 0;
823             }
824              
825             /* Main compile function */
826 278           int xs_jit_compile(pTHX_ const char *code, const char *name,
827             XS_JIT_Func *functions, int num_functions,
828             const char *cache_dir, int force,
829             const char *extra_cflags, const char *extra_ldflags) {
830             char so_path[MAX_PATH_LEN];
831             char c_path[MAX_PATH_LEN];
832             char dir_path[MAX_PATH_LEN];
833              
834             /* Check if already loaded in this process (unless force) */
835 278 100         if (!force && functions_already_loaded(aTHX_ functions, num_functions)) {
    100          
836 3           return 1; /* Already loaded, nothing to do */
837             }
838              
839             /* Build paths */
840 275           const char *dir = get_cache_dir(cache_dir);
841             char safe[256];
842 275           safe_name(name, safe, sizeof(safe));
843              
844 275           HV *config = get_hv("Config::Config", 0);
845 275 50         SV **dlext_sv = config ? hv_fetch(config, "dlext", 5, 0) : NULL;
846 275 50         const char *dlext = (dlext_sv && *dlext_sv) ? SvPV_nolen(*dlext_sv) : "so";
    50          
847              
848 275           snprintf(dir_path, sizeof(dir_path), "%s/lib/auto/%s", dir, safe);
849 275           snprintf(so_path, sizeof(so_path), "%s/%s.%s", dir_path, safe, dlext);
850 275           snprintf(c_path, sizeof(c_path), "%s/%s.c", dir_path, safe);
851              
852             /* Check cache unless force */
853 275 100         if (!force) {
854             Stat_t st;
855 274 50         if (PerlLIO_stat(so_path, &st) == 0) {
856             /* Cached - just load */
857 0           return xs_jit_load(aTHX_ name, so_path);
858             }
859             }
860              
861             /* Generate code */
862 275           char *generated = xs_jit_generate_code(aTHX_ code, name, functions, num_functions);
863 275 50         if (!generated) {
864 0           warn("XS::JIT: Failed to generate code");
865 0           return 0;
866             }
867              
868             /* Create cache directory */
869 275 50         if (!mkdir_p(dir_path)) {
870 0           warn("XS::JIT: Failed to create directory %s", dir_path);
871 0           free(generated);
872 0           return 0;
873             }
874              
875             /* Write C file */
876 275           FILE *fp = fopen(c_path, "w");
877 275 50         if (!fp) {
878 0           warn("XS::JIT: Failed to write %s: %s", c_path, strerror(errno));
879 0           free(generated);
880 0           return 0;
881             }
882 275           fputs(generated, fp);
883 275           fclose(fp);
884 275           free(generated);
885              
886             /* Compile with extra flags */
887 275 100         if (!xs_jit_compile_file(aTHX_ c_path, so_path, extra_cflags, extra_ldflags)) {
888 1           return 0;
889             }
890              
891             /* Load */
892 274           return xs_jit_load(aTHX_ name, so_path);
893             }