What Mnemo is
Mnemo compiles C to Kairos, a reversible concurrent language.
C destroys information all the time: x = 5 loses the old value of
x. Mnemo saves every overwritten value on a history stack, so the whole run
stays invertible. The output is plain Kairos with no VM extensions. On programs in the
supported subset, Mnemo aims for the same output and exit code as gcc.
#include <stdio.h>
int sq(int x) { return x * x; }
int main(void) {
int a[4] = {1, 2, 3, 4};
int s = 0;
for (int i = 0; i < 4; i++) s += sq(a[i]);
printf("%d\n", s); /* 30 */
return 0;
}
You can compile and run C in this page: Mnemo, the Kairos frontend and the Kairos VM run in your browser.
How it works
prog.c ─ gcc -E -DMNEMO ─ pycparser ─▶ AST
─ rewrites (early returns, switch, static, stdlib…)
─ layout: one memory cell per variable, array, field
─ lowering: reversible assignments, history, if/from
─ cells → Kairos array __mn_mem
─ library procedures (printing, bits, division…)
─ emit .kairos ─▶ Kairos VM
Reversibility
Every assignment becomes push(x, __mn_hist); x += 5: the old value goes onto
the history stack, and running backwards pops it again. C control flow becomes Kairos
if … fi and from … until with the exit assertions the inverse
needs. The cost is code size: the eight-line example above becomes about 1,200 lines of
Kairos. The playground shows the generated code for every program you run.
Memory
All named state (scalars, arrays, structs, globals) and the first part of the
malloc heap live in one Kairos array, __mn_mem, and a pointer is
an index into it. Kairos arrays cannot grow, so past the end of that array the heap
continues on a tape made of two Kairos stacks. free only reuses the most
recently allocated block.
--opt-uncall-user-calls
The history grows with every assignment. With this option, after call f(…)
Mnemo saves the cells f wrote, runs uncall f to empty the history
f produced, and puts the results back with an XOR swap. The result is the same
and long programs use much less memory. Recursive functions, functions with channels or
par, and some pointer writes are excluded.
The C subset
| Area | Supported |
|---|---|
| Types | int, unsigned, char, bool, short/long/long long (as int), size_t, int*_t/uint*_t, enum, struct, union, typedef. No floating point. |
| Pointers | void *, multi-level pointers, arithmetic, &x, &s.field, &a[i], malloc/calloc/free |
| Arrays | multidimensional, initialisers (designated too), run-time indices. No VLAs. |
| Control flow | if, switch, while, do, for, break, continue, return anywhere, ternary, short-circuit &&/||. No goto or setjmp. |
| Functions | recursion (mutual too), structs by value, function pointers. No user-defined variadic functions. |
stdio.h | printf (%d %i %u %x %X %o %c %s %p, width, flags), putchar, puts |
string.h | strlen, strcmp, strcpy, strcat, memcpy, memset, memmove, strdup and more |
The README has the
complete list, including stdlib.h, strings.h and
stdarg.h.
Parallelism
mnemo_pthread_parallel2(f, g) with two different functions becomes a Kairos
par block running two real threads. The branches share no writable memory, so a
global written by one branch and used by the other is a compile error. Branches communicate
through Kairos channels (mnemo_kairos_channel_t with
ssend/srecv), including channel passing.
try / rollback
Mnemo adds Kairos's try to C:
try (x < 10) {
x += step; /* runs forwards */
} rollback {
attempts++; /* if the condition is false: the body is undone, then this runs */
}
Try it
Pick an example, edit it if you like, and press Run. The first run downloads
the Python runtime that hosts Mnemo (about 10 MB), so it takes a few seconds. Start from
Blank to write your own program.
Under the output you get the exit code of main and how many VM cells the run
kept alive: at the end, on average and at the peak. Turn on
--opt-uncall-user-calls to watch those numbers drop. If main takes
argc, a field appears to set it.
Generated Kairos
More programs are in
tests/c/.
make test-gcc-compat runs 190 of them and checks that the output and exit code
match gcc.
Known differences from gcc
intis 64-bit, so signed overflow does not wrap at 32 bits.unsigneddoes wrap correctly modulo 232.sizeofa pointer is 4 (one cell).- Call arguments are evaluated left to right.
- No real I/O (files, input), no signals, no
errno. printfs from two parallel branches may interleave character by character.
Install locally
Mnemo needs Kairos, which provides the VM, and gcc, which it uses as the
preprocessor. One line installs both, Kairos first if it is missing, and asks for the sudo
password:
curl -fsSL https://github.com/nicologiuliani6/mnemo/releases/latest/download/install.sh | bash
Or install the packages from the releases by hand:
| Distribution | Install |
|---|---|
| Debian / Ubuntu | sudo apt install ./kairos_<version>_amd64.deb ./mnemo_<version>_amd64.deb |
| Fedora / RHEL | sudo dnf install ./kairos-<version>-1.<dist>.x86_64.rpm ./mnemo-<version>-1.<dist>.x86_64.rpm |
| Arch Linux | sudo pacman -U ./kairos-<version>-1-x86_64.pkg.tar.zst ./mnemo-<version>-1-x86_64.pkg.tar.zst |
From source
Clone Kairos next to Mnemo (or set KAIROS_ROOT=/path) and build it:
git clone https://github.com/nicologiuliani6/kairos.git
git clone https://github.com/nicologiuliani6/mnemo.git
(cd kairos && make install-deps && make)
cd mnemo && make venv # .venv + pip install -e .
Each release also ships a wheel: pip install mnemo-<version>-py3-none-any.whl.
Usage
mnemo prog.c [options] # compile to Kairos and run
mnemo dump-kairos prog.c [-o out.kairos] [--stdout] # write the generated Kairos
mnemo compile prog.c [-o exe] [--keep-kairos] # native executable: bytecode + libvm.so
mnemo prog.c prints the program's output and exits with the value returned by
main.
| Option | Effect |
|---|---|
--check-invertibility | runs main, then inverts it; the VM reports every step that cannot be undone |
--opt-uncall-user-calls | after each eligible call, frees the history it produced |
--auto | decides by itself whether to enable the option above |
--main-argc N | value of argc (also set with a // mnemo-main-argc: N line in the source) |
--vm-dump, --vm-stats | print the final VM dump / how many cells are still alive |
--kairos PATH | Kairos executable to use |
-v | print commands and exit codes on stderr |
Questions
Open an issue on the repository, or email me@nicologiuliani.site.