Meri is the back end of the Prabanta family of compilers, written in C with no dependencies beyond the C library. It reads the intermediate representation (IR) made by Limba, the front end, and runs it: a register-based virtual machine first, then AOT and JIT compilation.
Meri takes its ideas from SedaiBasic2, an earlier compiler of the author written in Free Pascal, not its code.
This is a project in development, not a usable program. meri reads
a module of the IR, compiles it to bytecode and runs it on a virtual
machine: no JIT and no AOT yet.
| Stage | Just started. The bytecode, its file format and the command line change without notice. |
| Platform | Developed and tested on Linux x86-64 only (Debian 13, GCC). |
luxia --emit=lir program.luxia # writes program.lir (Luxia)
meri program.lir [arguments] # runs it
meri --disasm program.lir # prints its bytecode
meri --summary program.lir # prints what the module holds
meri --lib-path=DIR program.lir # where its C libraries are looked for
A run-time error stops the program with a message on the standard error
and exit status 1; halt(code) exits with code. The language of the
module words its errors, luxia: index out of range at program.luxia:12:5;
Meri's own errors, such as a module it refuses, begin with meri:. A
program has the memory --max-memory gives it (by default half of the
physical memory): past it, "out of memory", never a crash. An error of
input or output is input/output error; a program whose output is closed
(program | head) ends with status 141 and no message.
What runs today: all of Luxia 0 that its front end compiles, the integers of any size (BigInt, on mini-gmp) and calls of C libraries declared in the program (x86-64 Linux only), with Meri's own calling convention, without libffi.
A module of the IR is trusted, as a precompiled chunk is in Lua. Meri
reads a .lir from a file with Limba's verifier first, and the verifier
checks the form of the module (types, SSA), not that its memory accesses
are safe. Those safety checks are in the IR itself: a front end such as
Luxia writes the checks of nil, of a block still alive and of the bounds,
and the optimizer keeps them. A module without them is a wrong module:
- an access of nil, within the first 64 KiB of memory, stops the run
(
wrong module: memory accessed with no check of nil); - any other unchecked access may crash the process or change the memory of the program or of the engine.
The fuzz target of meri defends the reader and the engine against a
malformed file, not against a valid module that does wrong things. Pointers
of the program are checked before the C library is given them.
meri program.lirruns the IR written by a front end, such asluxia --emit=lir.luxia program.luxiadoes both in one executable, in the repository of the language: the libraries are linked statically, and each function goes from the front end to the back end in memory, as soon as it is complete, with no file in between.- The virtual machine keeps one file of 64-bit registers, a window of them for each call, and fixed 4-byte instructions.
- Every program must print in Meri what it prints in the reference interpreter of Limba, on every engine.
include/meri/meri.h is Meri for another program: a module of the IR
compiled whole, or a function at a time as a front end gives it (Luxia's
limba/limba_luxia.h), each body read once, while it is still there;
the engine chosen when the compilation begins (meri_options); the C
libraries linked; the run, which only reads the program; its end flushed
and reported as meri reports it, the result owning what it holds
(meri_result_free). Luxia's tests/test_meri_api.c uses that header only, fed by
the front end of Luxia.
Meri needs Limba next to it
(../Limba, or LIMBA_DIR), at the commit named at the end of the last
commit message of Meri ("Limba: ..."): its build script builds Limba
first. A later Limba may change the IR; until Meri follows, it refuses
the modules of that Limba ("written against another runtime or operation
table").
./build.sh # release build
./build.sh release test # and the tests
./build.sh --help # variants (debug, asan, ubsan, tsan), actions
A C11 compiler (GCC or Clang) and a POSIX shell are all it needs.
GPL-3.0-or-later, see LICENSE. The authors, and the code of
others compiled into Meri (mini-gmp, under its own licences), are in
AUTHORS.