0

dependencies outside the Python standard library

import cimo
< 1 s

to compile a kernel file, then cached

the demo above compiles in your browser
~1.0×

the run time of gcc and clang -O3 on analyzable loops

gcc -O31.00cimo0.93
benchmarks →
~100×

faster than pure Python on stencils, flood fills, DP

Python252 mscimo3.4 ms
Usage

C kernels from Python

Compile a string or a .c file. Every non-static function becomes a Python callable, typed from its C signature. Arrays are passed by pointer and never copied.

  • Scripts and notebooks. The same call in a script, Jupyter, JupyterLite or marimo.
  • Cached. The second run of the same source starts instantly.
  • Buffers in place. numpy arrays, bytearray, memoryview and ctypes arrays.
Motivation

Native code at runtime, without a toolchain

Compute-graph engines, PDE solvers and symbolic code generators emit C at runtime and call the system compiler on the user's machine. Where there is none, they fail or fall back to slow paths. Numba avoids the compiler by bundling LLVM. cimo is a compiler written in Python: any machine that runs Python can compile and run C.

libraries

Pure-Python packages

Ship C kernels as source. No wheel matrix across OSes, architectures and Python versions, and no compiler needed for a source install.

apps

Desktop applications

Native-speed kernels in a frozen app, without bundling LLVM or installing a C compiler on every build machine.

sandboxed

Browsers and restricted machines

Pyodide, JupyterLite and marimo; plugin repositories that accept no binaries; machines where nothing can be installed.

Performance

Close to gcc and clang at -O3

cimo has a loop optimizer and a full instruction-selecting backend rather than a one-pass code generator.

  • Loop optimizer. Unrolling, unroll-and-jam, scalar replacement, strength reduction, runtime alias checks.
  • Backend. Instruction selection and register allocation, encoded to machine code in Python. No assembler, no linker.
  • Caching. Compiled modules are cached by content hash.
  • Optimization reports. cimo report lists which loops were optimized and why the others were not.
terminal
$ cimo report kernels.ckernels.c:12  loop j: optimized (unrolled x4, alias check src/dst)kernels.c:19  loop i,j: optimized (unroll-and-jam 2x4, scalar replacement)kernels.c:27  loop k: optimized (strength reduction, 3 loads hoisted)kernels.c:31  loop i: not optimized: base of `p` depends on a branchkernels.c:38  loop t: not optimized: y[t] depends on y[t-1]kernels.c:44  loop n: not optimized: calls `next_sample` (not inlined)3 of 6 loops optimized$ 
Language

C11 and a bundled C library

cimo ships its own libc, so kernels behave the same on every platform, including the browser.

  • Language. Structs, unions, bitfields, pointers of any depth, function pointers, recursion, varargs, VLAs, goto, switch, globals, const tables.
  • Headers. stdio.h, stdlib.h, string.h, math.h, ctype.h, inttypes.h and the freestanding headers. printf writes to sys.stdout, so output appears in notebooks.
  • SIMD. GNU vector types for NEON, AVX2 and AVX-512. #pragma omp simd and restrict honored as written.
  • System libraries. Link the host libc for files, clocks, threads and sockets, or any shared library such as zlib, BLAS or the Python C API.

Not supported: inline assembly, setjmp/longjmp, 128-bit long double. Each is a compile error that says so.

link
k = cimo.compile(src, link=["libc"], host_headers=True)
k = cimo.compile(src, link=["libz.so.1"])
k = cimo.load("kernels.c")   # or keep kernels in a file
C parameterPass from Python
float *, const uint8_t *, VLAsnumpy arrays, bytearray, memoryview
int, double, size_tPython numbers
struct pointa ctypes structure or a dict
Runtime

Threads and error handling

Kernels release the GIL and are safe to call concurrently. There is no launch API; use ordinary Python threads. malloc is lock-free, with a heap per thread.

16 threads, one kernel
from concurrent.futures import ThreadPoolExecutor

n = 16
with ThreadPoolExecutor() as pool:
    list(pool.map(lambda b: k.blur(b, n, h, w, src, dst), range(n)))

assert, abort(), exit() and traps unwind back to Python. Debug builds bounds-check every access and catch use-after-free, double free and leaks.

debug=True
k.blur(*src.shape, src, dst)
cimo.BoundsError: blur: read of src[4096][1] is out of bounds (src is 4096 x 4096)
  at line 5
WebAssembly

Train a network you draw

In Pyodide, JupyterLite and marimo, cimo compiles C to WebAssembly in the page and links it into Pyodide's memory. Here that runs a small deep-learning stack: draw a network, Python turns it into C for its forward and backward passes, cimo compiles the C, and it trains in your browser.

drag nodes to move · drag from a dot to a dot to connect · click a wire to cut it

Graphdrag and drop→ → → → TrainingAdam

Loads Pyodide and cimo (shared with the demo at the top).

step 0loss –accuracy –

or click the plot

In a notebook: await micropip.install("cimo"), then cimo.compile(src) as anywhere else. The code generator is one Python file.

Platforms

Supported platforms

PlatformBackendLoader
Linux arm64 / x86-64arm64, amd64mmap + mprotect (W^X)
macOS, Apple siliconarm64MAP_JIT, W^X fallback
Windows x64amd64VirtualAlloc + VirtualProtect
Pyodide / browserWebAssemblyshared-memory side module

Kernels run with the full privileges of your Python process, like ctypes. There is no sandbox.

Design

Architecture

One kernel through every stage of the compiler. This is cimo's real output for it, regenerated from the compiler.

cproc and QBE are ported to Python and checked against the originals by diffing their IR pass by pass. The loop optimizer and machine-code encoder are new. Every release is differential-tested against gcc and clang on generated random programs.

Contributing

cimo is developed in the open. Bug reports, benchmarks and pull requests are welcome.

pip install cimo
GitHub Docs