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.

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.

Cannoli Integration

Running a Perla-compiled web app under Cannoli's preforking HTTP server.

Compiling & Running

Perla has four main modes of invocation:

CommandWhat it does
perla script.plCompile and run. Cleans up the binary after.
perla -o prog script.plCompile to ./prog, don't run.
perla -c -o out.c script.plEmit C code only; skip the cc step.
perla -M Module.pmPrecompile 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

FlagEffect
-o FILEOutput path (executable in normal mode, C file with -c).
-cGenerate C only; don't invoke the C compiler.
-e 'CODE'Execute an inline one-liner.
-M FILEPrecompile module/directory.
-I PATHAdd to compile-time @INC.
--cc BINUse a specific C compiler (default gcc, alternative tcc).
--debugVerbose compile-time tracing. Prints module search, cache events, cc command.
--keepDon't delete the intermediate .c after compile.
-O0 .. -O3, -Os, -Ofastgcc optimization (default -O0; see below).
--precompile-depsAuto-build .pm.o+.pm.so for every module encountered.
--sharedPrefer .pm.so (runtime dlopen) over .pm.o (static link).
--force-rebuildperla -M rebuilds every module, even cached ones.
--vmRun via Strada codegen interpreter path (no cc step).
--stradaEmit Strada source instead of C.

Optimization levels

Defaults to -O0 — optimized for compile speed during dev. For release builds, use -O2 or -O3.

FlagAdded gcc argsUse case
(default)-O0 -wDev iteration (fastest compile)
-O2-O2 -w -fltoStandard release
-O3-O3 -w -flto -march=nativeMax performance, native CPU
-Os-Os -w -fltoSize-optimized
-Ofast-Ofast -w -flto -march=nativeAggressive (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.

PerlC identifier
$foov_foo
@arrv_arr
%hashv_hash
@_perla_at_
$_perla_dollar_underscore
$1..$9strada_capture_var(N)
$!strerror(errno) read / errno = N write
$/ $\ $,perla_irs perla_ors perla_ofs
$|__perla_autoflush (C int)
$$getpid()
$0perla_dollar_zero
$^W $^O $^V $^Xv__caret_W, v__caret_O, etc.
$]v__rbrack_
$Pkg::scalar (fully-qualified)perla_scalar_get("Pkg", "scalar")
@Pkg::arrperla_array_get("Pkg", "arr")
%Pkg::hashperla_hash_get("Pkg", "hash")

Runtime helpers available

All strada_* and perla_* functions are in scope. The most useful:

HelperUse
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_executeDBI bridge

Memory rules

  1. Tagged-int guard. Integers in range -2^62..2^62-1 are encoded directly in pointers. Any code that reads sv->type/sv->value/sv->meta/sv->refcount must check STRADA_IS_TAGGED_INT(sv) first. The high-level helpers handle this for you.
  2. strada_to_str allocates. Always free() the result. For no-alloc, use strada_to_str_buf with a stack buffer.
  3. Decref before reassign. strada_decref(v_result); v_result = strada_new_int(42); — forgetting leaks; double-free is worse.
  4. _take vs plain for container inserts. strada_hv_store_take(hv, "k", strada_new_str("v")) keeps refcount at 1; strada_hv_store increfs. Use _take for 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.

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:

  1. Directory of the input file
  2. Current directory (.)
  3. $PERLA5LIB env var
  4. $PERL5LIB (Perl compatibility)
  5. $HOME/perla/lib (if it exists)
  6. -I path CLI flags (prepended)
  7. 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/perla.conf, /etc/perla.conf. Also probes archlib subdirs (x86_64-linux/) for each path.

Dedup

Two layers prevent re-parsing:

The precompile cache

perla -M Foo/Bar.pm writes:

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.

Debugging & Diagnostics

--debug output vocabulary

Trace lineMeaning
use FooStarting 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-buildingNarrow heuristic matched, spawning perla -M
Foo: source /path (non-tree, will inline)Project-local module, inlining source
Foo: XS module detected — skippingFound 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

VarEffect
PERLA_DEBUG=1Same as --debug
PERLA_PERLPath to a real perl binary (for @INC fallback probe)
PERL5LIBCompile-time search path (colon-separated)
PERLA_LIBRuntime-require search path
PERLA_DBI_DEBUG=1Trace DBI method calls
PERLA_METHOD_DEBUG=1Trace method dispatch
PERLA_BUILD_PM_SO=1perla -M also builds .pm.so
PERLA_SHARED=1Same as --shared
PERLA_FORCE_REBUILD=1Same as --force-rebuild
PERLA_NO_AUTO_BUILD=1Disable narrow auto-build heuristic

Perl Compatibility

What works

What doesn't work (yet)

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:

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.

More

Markdown docs on GitHub →

The source-tree documentation has longer prose, internal notes, and memory-rule details.

Test suite →

68 tests covering the features Perla guarantees. Good reference for what definitely works.

Strada CLAUDE.md →

The Strada language reference that underlies Perla.

Back to Getting Started →

The 60-second install and first-program guide.