Compiling & Running
Flags, optimization levels, the compile-then-run loop, when to use -M vs a full compile.
__C__ Blocks
Drop into raw C from inside Perl. Variable name mapping, runtime helpers, memory rules.
Module Search Paths
How use and require resolve — compile-time vs runtime, dedup, auto-build.
Installing CPAN Modules
perla-cpan — fetch from MetaCPAN, resolve deps, compile XS, install into a prefix.
Debugging & Diagnostics
Reading --debug output, common failure modes, keeping the generated C.
Perl Compatibility
What works, what doesn't, where perla's semantics differ from real Perl.
Memory Management
Automatic cycle collection (no weaken needed) and the request arena, exposed to Perl via Strada::*.
Cannoli Integration
Running a perla-compiled web app under Cannoli's preforking HTTP server.
Compiling & Running
perla has four main modes of invocation:
| Command | What it does |
|---|---|
perla script.pl | Compile and run. Cleans up the binary after. |
perla -o prog script.pl | Compile to ./prog, don't run. |
perla -c -o out.c script.pl | Emit C code only; skip the cc step. |
perla -M Module.pm | Precompile the module to Module.pm.o. |
perla -M dir/ | Recursively precompile every .pm under dir/. |
perla -e 'code' | Run a one-liner. |
All command-line flags
| Flag | Effect |
|---|---|
-o FILE | Output path (executable in normal mode, C file with -c). |
-c | Generate C only; don't invoke the C compiler. |
-e 'CODE' | Execute an inline one-liner. |
-M FILE | Precompile module/directory. |
-I PATH | Add to compile-time @INC. |
--cc BIN | Use a specific C compiler (default gcc, alternative tcc). |
--debug | Verbose compile-time tracing. Prints module search, cache events, cc command. |
--keep | Don't delete the intermediate .c after compile. |
-O0 .. -O3, -Os, -Ofast | gcc optimization (default -O0; see below). |
--precompile-deps | Auto-build .pm.o+.pm.so for every module encountered. |
--shared | Prefer .pm.so (runtime dlopen) over .pm.o (static link). |
--force-rebuild | perla -M rebuilds every module, even cached ones. |
--vm | Run via Strada codegen interpreter path (no cc step). |
--strada | Emit Strada source instead of C. |
Optimization levels
Defaults to -O0 — optimized for compile speed during dev. For release builds, use -O2 or -O3.
| Flag | Added gcc args | Use case |
|---|---|---|
| (default) | -O0 -w | Dev iteration (fastest compile) |
-O2 | -O2 -w -flto | Standard release |
-O3 | -O3 -w -flto -march=native | Max performance, native CPU |
-Os | -Os -w -flto | Size-optimized |
-Ofast | -Ofast -w -flto -march=native | Aggressive (may violate strict IEEE) |
On a medium-sized web app, the gcc phase goes from ~15s at -O0 to ~100s at -O2. Worth it for release; not worth it during iteration.
__C__ Blocks
__C__ { ... } drops raw C into the generated output. perla doesn't parse or rewrite the block — it's emitted verbatim. Three valid positions:
File scope
Place outside any sub. Use for #includes, static state, and helper functions that you'll call from multiple __C__ blocks.
__C__ {
#include <sys/time.h>
#include <unistd.h>
static int call_counter = 0;
}
Inside a sub
Emitted as a { ... } block inside the generated C function body.
sub add { my ($a, $b) = @_; my $sum = 0; __C__ { int64_t __a = strada_to_int(v_a); int64_t __b = strada_to_int(v_b); strada_decref(v_sum); v_sum = strada_new_int(__a + __b); } return $sum; }
Expression context (GCC statement-expression)
my $pid = __C__ {
({ StradaValue *__r = strada_new_int((int64_t)getpid()); __r; })
};
Variable name mapping
Critical gotcha. Perl $foo becomes C v_foo — perla does not substitute $x → v_x inside __C__ blocks. You must use the C names directly. Use --keep to inspect the generated C if unsure.
| Perl | C identifier |
|---|---|
$foo | v_foo |
@arr | v_arr |
%hash | v_hash |
@_ | perla_at_ |
$_ | perla_dollar_underscore |
$1..$9 | strada_capture_var(N) |
$! | strerror(errno) read / errno = N write |
$/ $\ $, | perla_irs perla_ors perla_ofs |
$| | __perla_autoflush (C int) |
$$ | getpid() |
$0 | perla_dollar_zero |
$^W $^O $^V $^X | v__caret_W, v__caret_O, etc. |
$] | v__rbrack_ |
$Pkg::scalar (fully-qualified) | perla_scalar_get("Pkg", "scalar") |
@Pkg::arr | perla_array_get("Pkg", "arr") |
%Pkg::hash | perla_hash_get("Pkg", "hash") |
Runtime helpers available
All strada_* and perla_* functions are in scope. The most useful:
| Helper | Use |
|---|---|
strada_to_int(sv) | int64_t, handles tagged ints transparently |
strada_to_num(sv) | double |
strada_to_str(sv) | malloc'd char* — caller must free() |
strada_to_str_buf(sv, buf, sz) | writes to caller's buffer; no alloc |
strada_new_int(n) strada_new_num(d) strada_new_str(s) | construct boxed values |
strada_new_array() strada_new_hash() | empty containers |
strada_incref(sv) strada_decref(sv) | refcount bookkeeping |
strada_hv_store_take(hv, key, val) | hash insert, takes ownership |
strada_array_push_take(av, elem) | array append, takes ownership |
STRADA_IS_TAGGED_INT(sv) | Always check before touching sv->type or sv->value |
perla_bless(ref, "Pkg") perla_blessed(sv) | Perl blessing |
perla_method_dispatch(obj, method, args) | Full Perl method dispatch |
perla_scalar_get/set(pkg, name) | Package-scoped scalar via stash |
perla_dbi_connect, perla_dbi_prepare, perla_dbi_execute | DBI bridge |
Memory rules
- Tagged-int guard. Integers in range
-2^62..2^62-1are encoded directly in pointers. Any code that readssv->type/sv->value/sv->meta/sv->refcountmust checkSTRADA_IS_TAGGED_INT(sv)first. The high-level helpers handle this for you. strada_to_strallocates. Alwaysfree()the result. For no-alloc, usestrada_to_str_bufwith a stack buffer.- Decref before reassign.
strada_decref(v_result); v_result = strada_new_int(42);— forgetting leaks; double-free is worse. _takevs plain for container inserts.strada_hv_store_take(hv, "k", strada_new_str("v"))keeps refcount at 1;strada_hv_storeincrefs. Use_takefor freshly-allocated values, plain store for existing variables.
Complete worked example
use strict; __C__ { #include <sys/resource.h> } sub rss_bytes { my $r = 0; __C__ { struct rusage __u; if (getrusage(RUSAGE_SELF, &__u) == 0) { /* ru_maxrss is KB on Linux, bytes on macOS */ strada_decref(v_r); v_r = strada_new_int((int64_t)__u.ru_maxrss * 1024); } } return $r; } printf "RSS: %d MB\n", int(rss_bytes() / 1024 / 1024);
For the complete __C__ reference including FFI examples, see the Markdown version on GitHub.
Module Search Paths
perla has two separate search paths for use and require.
Compile-time path (consulted when parsing use X)
Built by _build_lib_paths, in search order:
- Directory of the input file
- Current directory (
.) $PERLA5LIBenv var$PERL5LIB(Perl compatibility)$HOME/perla/lib(if it exists)-I pathCLI flags (prepended)use lib "..."statements during parse
Not included by default: /opt/bzperl/... or any large CPAN tree. Including it would trigger transitive source parsing of every module in the tree, blowing compile time. Add it via use lib "..." in your script if you need it.
Runtime path (consulted for require and lazy-loads)
Baked into the executable at compile time. Plus at startup: $PERLA_LIB, ~/.perla.conf, /etc/perla.conf. Also probes archlib subdirs (x86_64-linux/) for each path.
Dedup
Two layers prevent re-parsing:
- Name-keyed:
use Foo::Bartwice gets caught by matching the Perl module name. - Path-keyed: two different
usestatements resolving to the same file (lib-path collisions) get caught by matching the resolved path.
The precompile cache
perla -M Foo/Bar.pm writes:
Foo/Bar.pm.o— object file for static linkingFoo/Bar.pm.deps— transitive dependency listFoo/Bar.pm.so— shared library (ifPERLA_BUILD_PM_SO=1or--shared)
Subsequent compiles that use this module pick up the cached .pm.o instead of re-parsing. Useful for warming a module tree:
$ perla -M lib/ # recursively precompile every .pm under lib/
Auto-build heuristic
By default, a module used in your program is only auto-precompiled if its source matches an import-installing pattern (sub import, our @EXPORT, *{$M.import}). Everything else is left for runtime require. This prevents runaway cascade compiles.
To precompile every dependency, use --precompile-deps.
Installing CPAN Modules
perla ships native implementations of the most-used modules (JSON, DBI, Carp, Data::Dumper, List::Util, Scalar::Util, Storable, Encode, File::Temp, Digest::*, MIME::Base64, Time::HiRes, and more). For everything else — pure-Perl and XS — there's perla-cpan.
| Tool | Purpose |
|---|---|
perla-cpan | End-user installer. Fetches a module by name from MetaCPAN, resolves transitive deps, compiles XS, installs .pm + .so into a prefix. |
perla-xs-build | Low-level XS compiler: turns a .xs into .o/.so against perla's perl.h shim. Called for you by perla-cpan. |
Quick start
# One-time: point perla at an install prefix $ export PERLA_LIB=$HOME/.perla/local/lib/perla5 $ export PERLA5LIB=$PERLA_LIB # Install what your program needs — dependencies resolved automatically $ perla-cpan Try::Tiny Path::Tiny JSON::MaybeXS # Compile and run as usual $ perla myprog.pl
Sources
perla-cpan accepts a module name (fetched from MetaCPAN), a pre-unpacked directory, or a local tarball:
$ perla-cpan Try::Tiny # by name, with transitive deps $ perla-cpan ~/build/My-Dist-1.0/ # pre-unpacked directory $ perla-cpan --deps ~/dl/Foo-Bar.tar.gz # local tarball, recurse deps
Useful flags
| Flag | Effect |
|---|---|
--prefix DIR | Install root (default $HOME/.perla/local). |
--no-deps | Skip the transitive-dependency recursion. |
--no-xs | Pure-Perl only; skip XS compilation entirely. |
--precompile | Also run perla -M on each installed .pm to build .pm.o/.pm.so sidecars. |
--dry-run | Show what would happen; install nothing. |
How it differs from cpanm. There's no live perl in the loop and no DynaLoader at runtime — XS is compiled ahead of time against perla's perl.h shim and linked into your binary. Most distributions build cleanly. A complex XS dist may reference a Perl-internals macro the shim doesn't define yet; that's usually a one-line addition. perla-cpan installs the current release and doesn't run a dist's Makefile.PL configure logic or satisfy version-range constraints.
Debugging & Diagnostics
--debug output vocabulary
| Trace line | Meaning |
|---|---|
use Foo | Starting to load module |
use Foo -> already loaded (skip) | Name-keyed dedup hit |
Foo: resolved to already-loaded path ... | Path-keyed dedup hit |
Foo: cache HIT /path [.pm.o (static)] | Found precompiled artifact, will link |
Foo: cache MISS /path — auto-building | Narrow heuristic matched, spawning perla -M |
Foo: source /path (non-tree, will inline) | Project-local module, inlining source |
Foo: XS module detected — skipping | Found auto/.../*.so, avoiding DynaLoader |
[cc] gcc ... | Full C compiler invocation (grep for this!) |
[cc] done in XX.XXs (ok) | cc finished with timing |
[require] ... | Runtime require activity (inside the compiled binary) |
Common failure modes
sh: 1: foo: not found after compile
The compiled binary wasn't on $PATH. Recent perla prefixes ./ automatically; if you see this, rebuild perla.
redefinition of 'perla_sub_Foo_bar' at link
Either the source defines the same sub twice (perla now matches Perl's last-wins semantic — rebuild perla), or a dependency cycle re-parses the file (caught by path-keyed dedup).
undefined reference to perla_mod_init_Xxx at link
A precompiled .pm.o was built with the wrong module name. Re-run perla -M /full/path/to/module.pm or set PERLA_MODULE_NAME=Foo::Bar explicitly.
Can't locate object method X via package "REF(0x0x...)"
A blessed ref was stringified for use as a class name. perla_bless should unwrap this via perla_class_name — rebuild perla if you see this today.
Compile works but program behaves weirdly
Run with --keep, grep the generated .c for the affected sub. Usually a codegen issue with a specific Perl idiom.
Environment variables
| Var | Effect |
|---|---|
PERLA_DEBUG=1 | Same as --debug |
PERLA_PERL | Path to a real perl binary (for @INC fallback probe) |
PERL5LIB | Compile-time search path (colon-separated) |
PERLA_LIB | Runtime-require search path |
PERLA_DBI_DEBUG=1 | Trace DBI method calls |
PERLA_METHOD_DEBUG=1 | Trace method dispatch |
PERLA_BUILD_PM_SO=1 | perla -M also builds .pm.so |
PERLA_SHARED=1 | Same as --shared |
PERLA_FORCE_REBUILD=1 | Same as --force-rebuild |
PERLA_NO_AUTO_BUILD=1 | Disable narrow auto-build heuristic |
Perl Compatibility
What works
- Core syntax: sigils, arrays, hashes, regex (
=~,s///,m//with/i,/s,/x,/g,/e),foreach/map/grep/sort,push/pop/shift/unshift, slice syntax, string interpolation,heredoc,qw//. - OO:
bless,@ISA,SUPER::,AUTOLOAD, multiple inheritance with Perl's method resolution order,overload("",==, arithmetic, etc.). - Moose-ish:
has,extends,with,before/after/around,isa,meta, auto-accessors — native C runtime implementation (not Class::MOP). - Exception handling:
eval { },die,$@,Try::Tiny, typedcatch. - Text & symbol-table features:
format/writereports (incl.$~/$^/$=/$%/$^A,formline),tie/untie/tiedon scalars, hashes, arrays, and filehandles, typeglobs and aliasing (*A = *B,*A = \$x),goto LABEL,statevariables,localon slices. - Full Unicode: UTF-8 string semantics (the
SVf_UTF8flag),Unicode::Normalize/utf8::NFC/NFD/NFKC/NFKD, andEncodetranscoding (Latin-1, CP1252, ASCII). Plus unsigned 64-bit (UV) integer arithmetic. - Core modules:
strict,warnings,Carp,Data::Dumper,JSON,YAML,URI::Escape,File::Spec,File::Temp,File::Find,List::Util,Scalar::Util,Time::HiRes,Encode,Digest::MD5/SHA,MIME::Base64,HTTP::Date,Storable::dclone. - DBI: connection, prepare, execute, bind, fetchrow_hashref/array, selectrow_array, transactions (
begin_work/commit/rollback), etc. SQLite, MySQL/MariaDB, and PostgreSQL drivers via native implementation. - CPAN modules: install pure-Perl and XS distributions with
perla-cpan— it fetches from MetaCPAN, resolves transitive deps, and compiles XS against perla'sperl.hshim. - DBIx::Class: a minimal stub in your
lib/DBIx/Class/provideshas_one/has_many/belongs_to,ResultSet::search/first/all,Bootstrap::Simple. Not the real DBIC — it's a re-implementation of its public API. - Drogo (web framework): works, including
Drogo::Server::Cannoli.
What doesn't work (yet)
eval STRINGat runtime. Has an opt-in path viacore::full_eval_on()but it shells out to a childperlacompile — slow and not ergonomic.eval BLOCKworks normally for exception handling.- Runtime
DynaLoader/XSLoaderbootstrap of arbitrary.sos. XS is compiled ahead of time byperla-cpanagainst perla'sperl.hshim and linked in — there's noperl-style runtime loader. Most distributions build cleanly; a complex one may surface a missing macro in the shim (usually a one-line addition). - CPAN version constraints &
Makefile.PLconfigure logic.perla-cpaninstalls the current release and readslib/directly; it doesn't run a dist's configure-time code or satisfy>=1.0, <2.0ranges. - Threads (Perl's
threadsmodule). perla is single-threaded per process; useforkfor concurrency (async::*helpers work). - Source filters. Parse happens before any runtime, so source filters that modify the source at
use-time don't apply. - Some corner-case Perl idioms. Deep symbol-table trickery,
localon some magical vars, baroqueoverloaddispatch. Most work; a fraction don't. Report them.
Memory Management
perla links Strada's shared runtime, which means Perl programs compiled with perla get two opt-in memory features that mainline Perl does not have. Both are compiled in by default and are no-ops in source if the runtime was built --without-cycle-gc or --without-arena.
Automatic cycle collection
A Bacon–Rajan synchronous cycle collector reclaims indirect reference cycles (array/hash/ref graphs) without Scalar::Util::weaken. It runs at a candidate-root threshold and once more at exit. Acyclic data is still freed deterministically via refcounting; only genuine cycles wait for the collector.
# Indirect cycle -- normal Perl leaks this; perla collects it automatically.
my $a = { name => "a" };
my $b = { name => "b" };
$a->{peer} = $b;
$b->{peer} = $a; # no weaken needed
Scalar::Util::weaken still works as before and is the right choice when you need deterministic teardown or are targeting a runtime built --without-cycle-gc. Cycles that close only through a closure capture or a tied container fall back to weaken; those aren't handled by the collector. Cyclic blessed objects reclaimed by the collector skip DESTROY — this matches Perl's own global-destruction behaviour for unreachable cycles.
Explicit controls are exposed under Strada:::
| Function | Description |
|---|---|
Strada::gc_collect() | Force a cycle collection now |
Strada::gc_disable() / Strada::gc_enable() | Toggle automatic collection |
Strada::gc_threshold($n) | Candidate-root count that triggers auto-collect (default 10000) |
Strada::gc_collections() | Stats: collections run |
Strada::gc_freed() | Stats: cyclic objects reclaimed |
Request arena
For request-scoped work (most obviously Cannoli’s preforking workers), the arena bump-allocates StradaValue structs into a region freed wholesale at arena_end — skipping per-object allocation and refcount teardown for the request's churn. Dormant until Strada::arena_begin() is called; safe to ship enabled.
Strada::arena_begin();
handle_request($req); # allocate freely; nothing freed individually
Strada::arena_end(); # reclaims everything allocated since begin
| Function | Description |
|---|---|
Strada::arena_begin() | Start a request arena (may be nested) |
Strada::arena_end() | Free the current arena wholesale |
Strada::arena_active() | 1 if an arena is open, 0 otherwise |
- Values created inside an arena must not escape
arena_end. Storing one into a longer-lived structure leaves a dangling reference. - An arena container that references a persistent (pre-arena) value leaks one reference on that value; keep arena data self-contained.
DESTROYis not invoked for objects reclaimed by the arena. Allocate file handles, sockets, mutexes, etc. outside the arena.- Single-threaded scope only. Cannoli's preforking model (one process per worker, single-threaded inside) is the intended use case.
- Block size is 128 KB (4096 StradaValue slots); blocks chain as needed.
See the Strada memory documentation for the deeper design notes (side-table cycle collector, ARENA_OWNS skip in strada_free_value, the cycle collector's cc_free_shell).
Cannoli Integration
Cannoli is the Strada-native preforking HTTP server. perla-compiled apps load into it via the cannoli_perla.so bridge.
$ cd /path/to/cannoli/lib/perla $ make # builds cannoli_perla.so $ make demo # builds Demo.pm.so $ /path/to/cannoli/cannoli \ --library "./cannoli_perla.so:\ so=$(pwd)/Demo.pm.so;\ init=perla_mod_init_Demo;\ handler=perla_sub_Demo_hello" \ --dev -p 8080 $ curl http://127.0.0.1:8080/ Hello from a perla-compiled handler!
The bridge parses three config options:
so=— path to the perla-compiled.pm.so(handler module)init=— module init function to call once at startup (usuallyperla_mod_init_Foo)handler=— per-request handler function (usuallyperla_sub_Foo_handle)
See the cannoli_perla source for full detail. Cannoli ships with a parallel cannoli_perl.so for running real Perl (via libperl) if you'd rather stay in that world.
Tip: wrap each request in Strada::arena_begin() / Strada::arena_end() (see Memory Management) for one-shot wholesale reclamation of the request's allocations. Cannoli's preforking model (single-threaded per worker) is the intended use case; nothing in the bridge prevents it from working out of the box.
More
Markdown docs on GitHub →
The source-tree documentation has longer prose, internal notes, and memory-rule details.
Test suite →
119 tests covering the features perla guarantees. Good reference for what definitely works.
Strada reference →
The Strada language reference that underlies perla.
Back to Getting Started →
The 60-second install and first-program guide.