cairo-native 0.2.6

A compiler to convert Cairo's intermediate representation Sierra code to MLIR.
<div align="center">

### ⚡ Cairo Native ⚡

A compiler to convert Cairo's intermediate representation "Sierra" code <br>
to machine code via MLIR and LLVM.

[Report Bug](https://github.com/lambdaclass/cairo_native/issues/new) · [Request Feature](https://github.com/lambdaclass/cairo_native/issues/new)

[![Telegram Chat][tg-badge]][tg-url]
[![rust](https://github.com/lambdaclass/cairo_native/actions/workflows/ci.yml/badge.svg)](https://github.com/lambdaclass/cairo_native/actions/workflows/ci.yml)
[![codecov](https://img.shields.io/codecov/c/github/lambdaclass/cairo_native)](https://codecov.io/gh/lambdaclass/cairo_native)
[![license](https://img.shields.io/github/license/lambdaclass/cairo_native)](/LICENSE)
[![pr-welcome]](#-contributing)
[![Crates.io Version](https://img.shields.io/crates/v/cairo_native)](https://crates.io/crates/cairo-native)


[tg-badge]: https://img.shields.io/endpoint?url=https%3A%2F%2Ftg.sumanjay.workers.dev%2FLambdaStarkNet%2F&logo=telegram&label=chat&color=neon
[tg-url]: https://t.me/LambdaStarkNet
[pr-welcome]: https://img.shields.io/static/v1?color=orange&label=PRs&style=flat&message=welcome

</div>

- [Getting Started]#getting-started
- [Included Tools]#included-tools
  - [Scripts]#scripts
  - [cairo-native-compile]#cairo-native-compile
  - [cairo-native-dump]#cairo-native-dump
  - [cairo-native-run]#cairo-native-run
  - [cairo-native-test]#cairo-native-test
  - [cairo-native-stress]#cairo-native-stress
  - [scarb-native-dump]#scarb-native-dump
  - [scarb-native-test]#scarb-native-test
- [Benchmarking]#benchmarking

For in-depth documentation, see the [developer documentation][].

## Disclaimer
🚧 Cairo Native is still being built therefore API breaking changes might happen
often so use it at your own risk. 🚧

For versions under `1.0` `cargo` doesn't comply with
[semver](https://semver.org/), so we advise to pin the version the version you
use. This can be done by adding `cairo-native = "0.1.0"` to your Cargo.toml

## Getting Started

### Dependencies
- Linux or macOS (aarch64 included) only for now
- LLVM 19 with MLIR: On debian you can use [apt.llvm.org]https://apt.llvm.org/,
  on macOS you can use brew
- Rust 1.78.0 or later, since we make use of the u128
  [abi change]https://blog.rust-lang.org/2024/03/30/i128-layout-update.html.
- Git

### Setup
> This step applies to all operating systems.

Run the following make target to install the dependencies (**both Linux and macOS**):

```bash
make deps
```

#### Linux
Since Linux distributions change widely, you need to install LLVM 19 via your
package manager, compile it or check if the current release has a Linux binary.

If you are on Debian/Ubuntu, check out the repository https://apt.llvm.org/
Then you can install with:

```bash
sudo apt-get install llvm-19 llvm-19-dev llvm-19-runtime clang-19 clang-tools-19 lld-19 libpolly-19-dev libmlir-19-dev mlir-19-tools
```

If you decide to build from source, here are some indications:

<details><summary>Install LLVM from source instructions</summary>

```bash
# Go to https://github.com/llvm/llvm-project/releases
# Download the latest LLVM 19 release:
# The blob to download is called llvm-project-19.x.x.src.tar.xz

# For example
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-19.1.5/llvm-project-19.1.5.src.tar.xz
tar xf llvm-project-19.1.5.src.tar.xz

cd llvm-project-19.1.5.src.tar
mkdir build
cd build

# The following cmake command configures the build to be installed to /opt/llvm-19
cmake -G Ninja ../llvm \
   -DLLVM_ENABLE_PROJECTS="mlir;clang;clang-tools-extra;lld;polly" \
   -DLLVM_BUILD_EXAMPLES=OFF \
   -DLLVM_TARGETS_TO_BUILD="Native" \
   -DCMAKE_INSTALL_PREFIX=/opt/llvm-19 \
   -DCMAKE_BUILD_TYPE=RelWithDebInfo \
   -DLLVM_PARALLEL_LINK_JOBS=4 \
   -DLLVM_ENABLE_BINDINGS=OFF \
   -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_ENABLE_LLD=ON \
   -DLLVM_ENABLE_ASSERTIONS=OFF

ninja install
```

</details>

Setup a environment variable called `MLIR_SYS_190_PREFIX`, `LLVM_SYS_191_PREFIX`
and `TABLEGEN_190_PREFIX` pointing to the llvm directory:

```bash
# For Debian/Ubuntu using the repository, the path will be /usr/lib/llvm-19
export MLIR_SYS_190_PREFIX=/usr/lib/llvm-19
export LLVM_SYS_191_PREFIX=/usr/lib/llvm-19
export TABLEGEN_190_PREFIX=/usr/lib/llvm-19
```

Alternatively, if installed from Debian/Ubuntu repository, then you can use
`env.sh` to automatically setup the environment variables.

```bash
source env.sh
```

#### MacOS
The makefile `deps` target (which you should have ran before) installs LLVM 19
with brew for you, afterwards you need to execute the `env.sh` script to setup
the needed environment variables.

```bash
source env.sh
```

### Make targets:
Running `make` by itself will check whether the required LLVM installation and
corelib is found, and then list available targets.

```bash
% make
LLVM is correctly set at /opt/homebrew/opt/llvm.
./scripts/check-corelib-version.sh 2.6.4
Usage:
    deps:         Installs the necesary dependencies.
    build:        Builds the cairo-native library and binaries in release mode.
    build-native: Builds cairo-native with the target-cpu=native rust flag.
    build-dev:    Builds cairo-native under a development-optimized profile.
    runtime:      Builds the runtime library required for AOT compilation.
    check:        Checks format and lints.
    test:         Runs all tests.
    proptest:     Runs property tests.
    coverage:     Runs all tests and computes test coverage.
    doc:          Builds documentation.
    doc-open:     Builds and opens documentation in browser.
    bench:        Runs the hyperfine benchmark script.
    bench-ci:     Runs the criterion benchmarks for CI.
    install:      Invokes cargo to install the cairo-native tools.
    clean:        Cleans the built artifacts.
    stress-test   Runs a command which runs stress tests.
    stress-plot   Plots the results of the stress test command.
    stress-clean  Clean the cache of AOT compiled code of the stress test command.
```

## Included Tools
Aside from the compilation and execution engine library, Cairo Native includes
a few command-line tools to aid development, and some useful scripts.

These are:
- The contents of the `/scripts/` folder
- `cairo-native-compile`
- `cairo-native-dump`
- `cairo-native-run`
- `cairo-native-test`
- `cairo-native-stress`
- `scarb-native-dump`
- `scarb-native-test`

### `cairo-native-compile`
```bash
Compiles a Cairo project outputting the generated MLIR and the shared library.
Exits with 1 if the compilation or run fails, otherwise 0.

Usage: cairo-native-compile [OPTIONS] <PATH> [OUTPUT_MLIR] [OUTPUT_LIBRARY]

Arguments:
  <PATH>            The Cairo project path to compile and run its tests
  [OUTPUT_MLIR]     The output path for the mlir, if none is passed, out.mlir will be the default
  [OUTPUT_LIBRARY]  If a path is passed, a dynamic library will be compiled and saved at that path

Options:
  -s, --single-file            Whether path is a single file
      --allow-warnings         Allows the compilation to succeed with warnings
  -r, --replace-ids            Replaces sierra ids with human-readable ones
  -O, --opt-level <OPT_LEVEL>  Optimization level, Valid: 0, 1, 2, 3. Values higher than 3 are considered as 3 [default: 0]
  -h, --help                   Print help
  -V, --version                Print version
```

###  `cairo-native-dump`
```bash
Usage: cairo-native-dump [OPTIONS] <INPUT>

Arguments:
  <INPUT>

Options:
  -o, --output <OUTPUT>  [default: -]
      --starknet         Compile a starknet contract
  -h, --help             Print help
```

### `cairo-native-run`
This tool allows to run programs using the JIT engine, like the `cairo-run`
tool, the parameters can only be felt values.

Example: `echo '1' | cairo-native-run 'program.cairo' 'program::program::main' --inputs - --outputs -`

```bash
Exits with 1 if the compilation or run fails, otherwise 0.

Usage: cairo-native-run [OPTIONS] <PATH>

Arguments:
  <PATH>  The Cairo project path to compile and run its tests

Options:
  -s, --single-file                    Whether path is a single file
      --allow-warnings                 Allows the compilation to succeed with warnings
      --available-gas <AVAILABLE_GAS>  In cases where gas is available, the amount of provided gas
      --run-mode <RUN_MODE>            Run with JIT or AOT (compiled) [default: jit] [possible values: aot, jit]
  -O, --opt-level <OPT_LEVEL>          Optimization level, Valid: 0, 1, 2, 3. Values higher than 3 are considered as 3 [default: 0]
  -h, --help                           Print help
  -V, --version                        Print version
```

### `cairo-native-test`
This tool mimics the `cairo-test`
[tool](https://github.com/starkware-libs/cairo/tree/main/crates/cairo-lang-test-runner)
and is identical to it in interface, the only feature it doesn't have is the profiler.

```bash
Compiles a Cairo project and runs all the functions marked as `#[test]`.
Exits with 1 if the compilation or run fails, otherwise 0.

Usage: cairo-native-test [OPTIONS] <PATH>

Arguments:
  <PATH>  The Cairo project path to compile and run its tests

Options:
  -s, --single-file            Whether path is a single file
      --allow-warnings         Allows the compilation to succeed with warnings
  -f, --filter <FILTER>        The filter for the tests, running only tests containing the filter string [default: ]
      --include-ignored        Should we run ignored tests as well
      --ignored                Should we run only the ignored tests
      --starknet               Should we add the starknet plugin to run the tests
      --run-mode <RUN_MODE>    Run with JIT or AOT (compiled) [default: jit] [possible values: aot, jit]
  -O, --opt-level <OPT_LEVEL>  Optimization level, Valid: 0, 1, 2, 3. Values higher than 3 are considered as 3 [default: 0]
  -h, --help                   Print help
  -V, --version                Print version
```

For single files, you can use the `-s, --single-file` option.

For a project, it needs to have a `cairo_project.toml` specifying the
`crate_roots`. You can find an example under the `cairo-tests/` folder, which
is a cairo project that works with this tool.

```bash
cairo-native-test -s myfile.cairo

cairo-native-test ./cairo-tests/
```

This will run all the tests (functions marked with the `#[test]` attribute).

### `cairo-native-stress`
This tool runs a stress test on Cairo Native.

```bash
A stress tester for Cairo Native

It compiles Sierra programs with Cairo Native, caches, and executes them with AOT runner. The compiled dynamic libraries are stored in `AOT_CACHE_DIR` relative to the current working directory.

Usage: cairo-native-stress [OPTIONS] <ROUNDS>

Arguments:
  <ROUNDS>
          Amount of rounds to execute

Options:
  -o, --output <OUTPUT>
          Output file for JSON formatted logs

  -h, --help
          Print help (see a summary with '-h')
```

To quickly run a stress test and save logs as json, run:
```bash
make stress-test
```

This takes a lot of time to finish (it will probably crash first), you can kill
the program at any time.

To plot the results, run:
```bash
make stress-plot
```

To clear the cache directory, run:
```bash
make stress-clean
```

### `scarb-native-dump`
This tool mimics the `scarb build` [command](https://github.com/software-mansion/scarb/tree/main/extensions/scarb-cairo-test).
You can download it on our [releases](https://github.com/lambdaclass/cairo_native/releases) page.

This tool should be run at the directory where a `Scarb.toml` file is and it will
behave like `scarb build`, leaving the MLIR files under the `target/` folder
besides the generated JSON sierra files.

### `scarb-native-test`
This tool mimics the `scarb test` [command](https://github.com/software-mansion/scarb/tree/main/extensions/scarb-cairo-test).
You can download it on our [releases](https://github.com/lambdaclass/cairo_native/releases) page.

```bash
Compiles all packages from a Scarb project matching `packages_filter` and
runs all functions marked with `#[test]`. Exits with 1 if the compilation
or run fails, otherwise 0.

Usage: scarb-native-test [OPTIONS]

Options:
  -p, --package <SPEC>         Packages to run this command on, can be a concrete package name (`foobar`) or a prefix glob (`foo*`) [env: SCARB_PACKAGES_FILTER=] [default: *]
  -w, --workspace              Run for all packages in the workspace
  -f, --filter <FILTER>        Run only tests whose name contain FILTER [default: ]
      --include-ignored        Run ignored and not ignored tests
      --ignored                Run only ignored tests
      --run-mode <RUN_MODE>    Run with JIT or AOT (compiled) [default: jit] [possible values: aot, jit]
  -O, --opt-level <OPT_LEVEL>  Optimization level, Valid: 0, 1, 2, 3. Values higher than 3 are considered as 3 [default: 0]
  -h, --help                   Print help
  -V, --version                Print version
```

## Benchmarking

### Requirements
- [hyperfine]https://github.com/sharkdp/hyperfine: `cargo install hyperfine`
- [cairo 2.9.0-dev.0]https://github.com/starkware-libs/cairo
- Cairo Corelibs
- LLVM 19 with MLIR

You need to setup some environment variables:

```bash
$MLIR_SYS_190_PREFIX=/path/to/llvm19  # Required for non-standard LLVM install locations.
$LLVM_SYS_191_PREFIX=/path/to/llvm19  # Required for non-standard LLVM install locations.
$TABLEGEN_190_PREFIX=/path/to/llvm19  # Required for non-standard LLVM install locations.
```

You can then run the `bench` makefile target:

```bash
make bench
```

The `bench` target will run the `./scripts/bench-hyperfine.sh` script.
This script runs hyperfine commands to compare the execution time of programs in the `./programs/benches/` folder.
Each program is compiled and executed via the execution engine with the `cairo-native-run` command and via the cairo-vm with the `cairo-run` command provided by the `cairo` codebase.
The `cairo-run` command should be available in the `$PATH` and ideally compiled with `cargo build --release`.
If you want the benchmarks to run using a specific build, or the `cairo-run` commands conflicts with something (e.g. the cairo-svg package binaries in macos) then the command to run `cairo-run` with a full path can be specified with the `$CAIRO_RUN` environment variable.

[developer documentation]: https://lambdaclass.github.io/cairo_native/cairo_native/docs/index.html