File Coverage

blib/lib/XS/JIT.pm
Criterion Covered Total %
statement 14 31 45.1
branch 0 10 0.0
condition 0 5 0.0
subroutine 5 10 50.0
pod 4 4 100.0
total 23 60 38.3


line stmt bran cond sub pod time code
1             package XS::JIT;
2              
3 40     40   6401737 use 5.008003;
  40         111  
4 40     40   1879 use strict;
  40         87  
  40         2357  
5 40     40   192 use warnings;
  40         60  
  40         1898  
6 40     40   177 use File::Spec;
  40         100  
  40         1011  
7 40     40   147 use Config;
  40         73  
  40         17737  
8              
9             our $VERSION = '0.17';
10              
11             require XSLoader;
12             XSLoader::load('XS::JIT', $VERSION);
13              
14             # Find the XS::JIT installation directory (where headers and libs are)
15             sub _find_xs_jit_dir {
16             my @search_paths = (
17             # Standard installation locations
18             File::Spec->catdir($Config{installsitearch}, 'auto', 'XS', 'JIT'),
19             File::Spec->catdir($Config{installvendorarch} || '', 'auto', 'XS', 'JIT'),
20             File::Spec->catdir($Config{installarchlib}, 'auto', 'XS', 'JIT'),
21             # Local lib (for local::lib users)
22             (exists $ENV{PERL_LOCAL_LIB_ROOT}
23 0 0 0 0     ? File::Spec->catdir($ENV{PERL_LOCAL_LIB_ROOT}, 'lib', 'perl5', $Config{archname}, 'auto', 'XS', 'JIT')
24             : ()),
25             # Relative to this module (for uninstalled/development use)
26             File::Spec->catdir(File::Spec->rel2abs(__FILE__), '..', '..', '..', '..', 'lib', 'XS', 'JIT'),
27             );
28              
29 0           for my $dir (@search_paths) {
30 0 0 0       next unless defined $dir && length $dir;
31 0           my $header = File::Spec->catfile($dir, 'xs_jit.h');
32 0 0         return $dir if -f $header;
33             }
34 0           return undef;
35             }
36              
37             sub inc_dir {
38 0     0 1   my $dir = _find_xs_jit_dir();
39 0 0         die "Cannot find XS::JIT installation directory with xs_jit.h" unless $dir;
40 0           return $dir;
41             }
42              
43             sub cflags {
44 0     0 1   my $dir = inc_dir();
45 0           return "-I$dir";
46             }
47              
48             sub libs {
49 0     0 1   my $dir = inc_dir();
50             # LIBS expects -L and -l format
51 0           return "-L$dir -lxs_jit";
52             }
53              
54             sub static_libs {
55 0     0 1   my $dir = inc_dir();
56 0           my $lib = File::Spec->catfile($dir, 'libxs_jit.a');
57 0 0         return $lib if -f $lib;
58 0           return undef;
59             }
60              
61             =head1 NAME
62              
63             XS::JIT - Lightweight JIT compiler for XS code
64              
65             =head1 VERSION
66              
67             Version 0.03
68              
69             =head1 SYNOPSIS
70              
71             use XS::JIT;
72              
73             # Compile C code and install functions
74             XS::JIT->compile(
75             code => $c_code,
76             name => 'MyModule::JIT::Foo',
77             functions => {
78             # Simple form - XS::JIT generates a wrapper
79             'Foo::get' => 'jit_get',
80              
81             # Hashref form - for XS-native functions (no wrapper)
82             'Foo::new' => { source => 'jit_new', is_xs_native => 1 },
83             'Foo::name' => { source => 'jit_name', is_xs_native => 1 },
84             },
85             cache_dir => '_CACHED_XS', # optional, defaults to _CACHED_XS
86             force => 0, # optional, force recompile
87             );
88              
89             =head1 DESCRIPTION
90              
91             XS::JIT is a lightweight alternative to Inline::C for runtime JIT compilation
92             of XS code. It's specifically optimized for use cases where you're generating
93             C code dynamically and need to compile and load it at runtime.
94              
95             Unlike Inline::C, XS::JIT:
96              
97             =over 4
98              
99             =item * Skips C code parsing (no Parse::RecDescent dependency)
100              
101             =item * Skips xsubpp (generates C code directly)
102              
103             =item * Uses direct compiler invocation (no make/Makefile.PL)
104              
105             =item * Provides a C API for use from other XS modules
106              
107             =back
108              
109             =head1 METHODS
110              
111             =head2 compile
112              
113             my $ok = XS::JIT->compile(%options);
114              
115             Compiles C code and installs the specified functions into Perl namespaces.
116              
117             Options:
118              
119             =over 4
120              
121             =item code (required)
122              
123             The C source code to compile. This should be valid C code that uses the
124             Perl C API (EXTERN.h, perl.h, XSUB.h are included automatically).
125              
126             =item name (required)
127              
128             A unique module name for caching purposes (e.g., "MyApp::JIT::Class_0").
129             This is used to generate the boot function name and cache path.
130              
131             =item functions (required)
132              
133             A hashref mapping target Perl function names to source C function names.
134             Values can be either a simple string (the C function name) or a hashref
135             with options:
136              
137             # Simple string form
138             functions => {
139             'Package::method' => 'c_function_name',
140             }
141              
142             # Hashref form with options
143             functions => {
144             'Package::method' => {
145             source => 'c_function_name',
146             is_xs_native => 1, # function handles XS stack itself
147             },
148             }
149              
150             The C option is important for performance. Set it to 1 when
151             your C function is already written as a proper XS function using C,
152             C, C, and C. This tells XS::JIT to create a simple
153             alias instead of generating a wrapper function, avoiding any overhead.
154              
155             =item cache_dir
156              
157             Optional. Directory for caching compiled modules. Defaults to "_CACHED_XS".
158              
159             =item force
160              
161             Optional. If true, forces recompilation even if a cached version exists.
162              
163             =back
164              
165             Returns 1 on success, 0 on failure.
166              
167             =head2 is_cached
168              
169             my $cached = XS::JIT->is_cached($code, $name, $cache_dir);
170              
171             Checks if a compiled module exists in the cache.
172              
173             Arguments:
174              
175             =over 4
176              
177             =item $code
178              
179             The C source code.
180              
181             =item $name
182              
183             The module name.
184              
185             =item $cache_dir
186              
187             Optional. Cache directory. Defaults to "_CACHED_XS".
188              
189             =back
190              
191             Returns 1 if cached, 0 otherwise.
192              
193             =head2 generate_code
194              
195             my $c_code = XS::JIT->generate_code($user_code, $name, \%functions);
196              
197             Generates the complete C source code with XS wrappers and boot function,
198             without compiling it. Useful for debugging or custom build processes.
199              
200             Arguments:
201              
202             =over 4
203              
204             =item $user_code
205              
206             The user's C source code.
207              
208             =item $name
209              
210             The module name (used for boot function naming).
211              
212             =item \%functions
213              
214             A hashref mapping Perl function names to C function names.
215              
216             =back
217              
218             Returns the complete generated C code as a string.
219              
220             =head2 inc_dir
221              
222             my $dir = XS::JIT->inc_dir();
223              
224             Returns the directory containing the XS::JIT header file (xs_jit.h).
225             This is useful for modules that want to use the XS::JIT C API.
226              
227             Dies if the installation directory cannot be found.
228              
229             =head2 cflags
230              
231             my $cflags = XS::JIT->cflags();
232              
233             Returns compiler flags needed to compile code that uses the XS::JIT C API.
234             Currently returns C<-I/path/to/xs_jit.h>.
235              
236             =head2 libs
237              
238             my $libs = XS::JIT->libs();
239              
240             Returns linker flags needed for XS::JIT in the format C<-L/path -lxs_jit>.
241              
242             =head2 static_libs
243              
244             my $lib_path = XS::JIT->static_libs();
245              
246             Returns the full path to the static library C, or C if
247             not found. Useful for build systems that prefer explicit static linking.
248              
249             Use both C and C in your Makefile.PL:
250              
251             use XS::JIT;
252             WriteMakefile(
253             ...
254             INC => XS::JIT->cflags(),
255             LIBS => [XS::JIT->libs()],
256             );
257              
258             =head1 WRITING C FUNCTIONS
259              
260             XS::JIT supports two styles of C functions: wrapper-style functions that
261             return an SV*, and XS-native functions that handle the stack directly.
262              
263             =head2 Wrapper-Style Functions (default)
264              
265             These functions take C as the first argument and return an C.
266             XS::JIT generates a wrapper that handles the Perl stack:
267              
268             SV* my_getter(SV* self) {
269             dTHX;
270             /* self is the invocant (class or object) */
271             return newSVpv("hello", 0);
272             }
273              
274             =head2 XS-Native Functions (recommended for performance)
275              
276             For best performance, write functions using the XS conventions directly
277             and set C 1> in the function mapping. This avoids
278             wrapper overhead entirely:
279              
280             XS_EUPXS(my_getter) {
281             dVAR; dXSARGS;
282             PERL_UNUSED_VAR(cv);
283             SV* self = ST(0);
284             HV* hv = (HV*)SvRV(self);
285             SV** valp = hv_fetch(hv, "value", 5, 0);
286             ST(0) = (valp && *valp) ? *valp : &PL_sv_undef;
287             XSRETURN(1);
288             }
289              
290             Register with:
291              
292             functions => {
293             'Package::getter' => { source => 'my_getter', is_xs_native => 1 },
294             }
295              
296             =head2 Functions with Variable Arguments
297              
298             Use C or C to access additional arguments:
299              
300             SV* my_setter(SV* self, ...) {
301             JIT_ARGS;
302              
303             if (items < 2) {
304             croak("Value required");
305             }
306              
307             SV* value = ST(1); /* First argument after self */
308             /* ... do something with value ... */
309             return newSVsv(value);
310             }
311              
312             =head2 Returning Self for Method Chaining
313              
314             When returning C for method chaining, you must increment the
315             reference count:
316              
317             SV* my_chainable(SV* self, ...) {
318             JIT_ARGS;
319             /* ... modify object ... */
320             SvREFCNT_inc(self);
321             return self;
322             }
323              
324             =head2 Creating Objects
325              
326             SV* my_constructor(SV* class_sv, ...) {
327             dTHX;
328             const char* classname = SvPV_nolen(class_sv);
329             HV* self_hv = newHV();
330              
331             /* Store attributes */
332             hv_store(self_hv, "attr", 4, newSViv(0), 0);
333              
334             /* Bless and return */
335             return sv_bless(newRV_noinc((SV*)self_hv),
336             gv_stashpv(classname, GV_ADD));
337             }
338              
339             =head1 C API
340              
341             XS::JIT provides a C API that can be used directly from other XS modules
342             without Perl stack overhead. Include the header file:
343              
344             #include "xs_jit.h"
345              
346             The function mapping structure:
347              
348             typedef struct {
349             const char *target; /* "Package::funcname" - where to install */
350             const char *source; /* "c_func_name" - function in user's C code */
351             int has_varargs; /* 1 if function takes variable arguments */
352             int is_xs_native; /* 1 if function is already XS-native */
353             } XS_JIT_Func;
354              
355             Example usage:
356              
357             XS_JIT_Func funcs[] = {
358             { "Foo::new", "jit_new", 0, 1 }, /* XS-native, no wrapper */
359             { "Foo::name", "jit_name", 0, 1 }, /* XS-native, no wrapper */
360             { NULL, NULL, 0, 0 }
361             };
362              
363             xs_jit_compile(aTHX_ c_code, "MyModule::JIT::Foo",
364             funcs, 2, NULL, 0);
365              
366             Set C to 1 when your functions use C, C,
367             and C directly. This creates simple aliases instead of wrappers.
368              
369             The header file location can be found programmatically:
370              
371             use XS::JIT;
372             print XS::JIT->inc_dir(), "\n";
373              
374             Or use the C method in your Makefile.PL:
375              
376             use XS::JIT;
377             WriteMakefile(
378             ...
379             INC => XS::JIT->cflags(),
380             );
381              
382             =head1 CONVENIENCE MACROS
383              
384             =head2 JIT_ARGS
385              
386             The C macro initializes both the thread context and the XS argument
387             stack in a single statement. Use this at the start of functions that need to
388             access variable arguments:
389              
390             SV* my_function(SV* self, ...) {
391             JIT_ARGS; /* expands to: dTHX; dXSARGS */
392              
393             if (items < 2) croak("Need at least one argument");
394             SV* arg = ST(1);
395             return newSVsv(arg);
396             }
397              
398             This is equivalent to:
399              
400             SV* my_function(SV* self, ...) {
401             dTHX;
402             dXSARGS;
403             ...
404             }
405              
406             =head1 INLINE::C COMPATIBILITY
407              
408             XS::JIT provides the following macros for compatibility with code written
409             for Inline::C:
410              
411             Inline_Stack_Vars - equivalent to dXSARGS
412             Inline_Stack_Items - number of arguments (items)
413             Inline_Stack_Item(x) - get argument x (ST(x))
414             Inline_Stack_Reset - reset stack pointer (sp = mark)
415             Inline_Stack_Push(x) - push value onto stack (XPUSHs(x))
416             Inline_Stack_Done - finalize stack (PUTBACK)
417             Inline_Stack_Return(x) - return x values (XSRETURN(x))
418             Inline_Stack_Void - return no values (XSRETURN(0))
419              
420              
421             =head1 BENCHMARK
422              
423             ============================================================
424             XS::JIT vs Inline::C Benchmark
425             ============================================================
426              
427             Testing XS::JIT...
428             ----------------------------------------
429             First compile: 0.3311 seconds
430             Already loaded: 0.000026 seconds
431             Runtime (10k iterations): 0.0094 seconds
432              
433             Testing Inline::C...
434             ----------------------------------------
435             First compile: 0.7568 seconds
436             Runtime (10k iterations): 0.0127 seconds
437              
438             ============================================================
439             Summary
440             ============================================================
441             First compile speedup: 2.3x faster
442             Runtime performance: 1.36x faster (XS::JIT)
443              
444             =head1 SEE ALSO
445              
446             L - The original runtime C compiler for Perl (which has more features)
447              
448             L - XS language reference
449              
450             L - Perl internal functions for XS programming
451              
452             L - Perl API listing
453              
454             =head1 AUTHOR
455              
456             LNATION
457              
458             =head1 LICENSE AND COPYRIGHT
459              
460             This software is Copyright (c) 2026 by LNATION.
461              
462             This is free software, licensed under:
463              
464             The Artistic License 2.0 (GPL Compatible)
465              
466             =cut
467              
468             1;