Code Monkey home page Code Monkey logo

cairo_native's Introduction

⚡ Cairo Native ⚡

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

Report Bug · Request Feature

Telegram Chat rust codecov license pr-welcome

To get started on how to setup and run cairo-native check the getting started section.

To read more in-depth documentation, visit this page.

⚠️ 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, 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

Implemented Library Functions

Cairo Native works by leveraging the intermediate representation of Cairo called Sierra. Sierra uses a list of builtin functions that implement the language functionality, those are called library functions, short: libfuncs. Basically every statement in a sierra program is a call to a libfunc, thus they are the core of Cairo Native progress towards feature parity.

This is a list of the current progress implementing each libfunc.

Implemented libfuncs (click to open)
  1. alloc_local
  2. array_append
  3. array_get
  4. array_len
  5. array_new
  6. array_pop_front_consume
  7. array_pop_front
  8. array_slice
  9. array_snapshot_pop_back
  10. array_snapshot_pop_front
  11. bitwise
  12. bool_and_impl
  13. bool_not_impl
  14. bool_or_impl
  15. bool_to_felt252
  16. bool_xor_impl
  17. box_forward_snapshot
  18. branch_align
  19. bytes31_const
  20. bytes31_to_felt252
  21. bytes31_try_from_felt252
  22. call_contract_syscall (StarkNet)
  23. class_hash_to_felt252 (StarkNet)
  24. class_hash_try_from_felt252 (StarkNet)
  25. contract_address_const (StarkNet)
  26. contract_address_to_felt252 (StarkNet)
  27. contract_address_try_from_felt252 (StarkNet)
  28. deploy_syscall (StarkNet)
  29. disable_ap_tracking
  30. downcast
  31. drop (3)
  32. dup (3)
  33. ec_neg
  34. ec_point_from_x_nz
  35. ec_point_is_zero
  36. ec_point_try_new_nz
  37. ec_point_unwrap
  38. ec_point_zero
  39. ec_state_add_mul
  40. ec_state_add
  41. ec_state_init
  42. ec_state_try_finalize_nz
  43. emit_event_syscall (StarkNet)
  44. enable_ap_tracking
  45. enum_from_bounded_int
  46. enum_init
  47. enum_match
  48. enum_snapshot_match
  49. felt252_add_const (4)
  50. felt252_add
  51. felt252_const
  52. felt252_dict_entry_finalize
  53. felt252_dict_entry_get
  54. felt252_dict_new
  55. felt252_dict_squash
  56. felt252_div_const (4)
  57. felt252_div (4)
  58. felt252_is_zero
  59. felt252_mul_const (4)
  60. felt252_mul
  61. felt252_sub_const (4)
  62. felt252_sub
  63. finalize_locals
  64. function_call
  65. get_available_gas
  66. get_block_hash_syscall (StarkNet)
  67. get_builtin_costs (5)
  68. get_execution_info_syscall (StarkNet)
  69. hades_permutation
  70. i128_diff
  71. i16_diff
  72. i32_diff
  73. i64_diff
  74. i8_diff
  75. into_box (2)
  76. jump
  77. keccak_syscall (StarkNet)
  78. library_call_syscall (StarkNet)
  79. match_nullable
  80. null
  81. nullable_forward_snapshot
  82. nullable_from_box
  83. pedersen
  84. print
  85. rename
  86. replace_class_syscall (StarkNet)
  87. revoke_ap_tracking
  88. send_message_to_l1_syscall (StarkNet)
  89. snapshot_take (1)
  90. span_from_tuple
  91. storage_address_from_base_and_offset (StarkNet)
  92. storage_address_from_base (StarkNet)
  93. storage_address_to_felt252 (StarkNet)
  94. storage_address_try_from_felt252 (StarkNet)
  95. storage_base_address_const (StarkNet)
  96. storage_base_address_from_felt252 (StarkNet)
  97. storage_read_syscall (StarkNet)
  98. storage_write_syscall (StarkNet)
  99. store_local
  100. store_temp
  101. struct_construct
  102. struct_deconstruct
  103. struct_snapshot_deconstruct
  104. u128_byte_reverse
  105. u128_const
  106. u128_eq
  107. u128_guarantee_mul
  108. u128_is_zero
  109. u128_mul_guarantee_verify
  110. u128_overflowing_add
  111. u128_overflowing_sub
  112. u128_safe_divmod
  113. u128_sqrt
  114. u128_to_felt252
  115. u128s_from_felt252
  116. u16_const
  117. u16_eq
  118. u16_is_zero
  119. u16_overflowing_add
  120. u16_overflowing_sub
  121. u16_safe_divmod
  122. u16_sqrt
  123. u16_to_felt252
  124. u16_try_from_felt252
  125. u16_wide_mul
  126. u256_is_zero
  127. u256_safe_divmod
  128. u256_sqrt
  129. u32_const
  130. u32_eq
  131. u32_is_zero
  132. u32_overflowing_add
  133. u32_overflowing_sub
  134. u32_safe_divmod
  135. u32_sqrt
  136. u32_to_felt252
  137. u32_try_from_felt252
  138. u32_wide_mul
  139. u64_const
  140. u64_eq
  141. u64_is_zero
  142. u64_overflowing_add
  143. u64_overflowing_sub
  144. u64_safe_divmod
  145. u64_sqrt
  146. u64_to_felt252
  147. u64_try_from_felt252
  148. u64_wide_mul
  149. u8_const
  150. u8_eq
  151. u8_is_zero
  152. u8_overflowing_add
  153. u8_overflowing_sub
  154. u8_safe_divmod
  155. u8_sqrt
  156. u8_to_felt252
  157. u8_try_from_felt252
  158. u8_wide_mul
  159. unbox (2)
  160. unwrap_non_zero
  161. upcast
  162. withdraw_gas_all (5)
  163. withdraw_gas (5)
Not yet implemented libfuncs (click to open)
  1. const_as_box
  2. secp256k1_add_syscall (StarkNet)
  3. secp256k1_get_point_from_x_syscall (StarkNet)
  4. secp256k1_get_xy_syscall (StarkNet)
  5. secp256k1_mul_syscall (StarkNet)
  6. secp256k1_new_syscall (StarkNet)
  7. secp256r1_add_syscall (StarkNet)
  8. secp256r1_get_point_from_x_syscall (StarkNet)
  9. secp256r1_get_xy_syscall (StarkNet)
  10. secp256r1_mul_syscall (StarkNet)
  11. secp256r1_new_syscall (StarkNet)
Not yet implemented libfuncs (testing category only, click to open) Testing libfuncs:
  1. pop_log (StarkNet, testing)
  2. redeposit_gas
  3. set_account_contract_address (StarkNet, testing)
  4. set_block_number (StarkNet, testing)
  5. set_block_timestamp (StarkNet, testing)
  6. set_caller_address (StarkNet, testing)
  7. set_chain_id (StarkNet, testing)
  8. set_contract_address (StarkNet, testing)
  9. set_max_fee (StarkNet, testing)
  10. set_nonce (StarkNet, testing)
  11. set_sequencer_address (StarkNet, testing)
  12. set_signature (StarkNet, testing)
  13. set_transaction_hash (StarkNet, testing)
  14. set_version (StarkNet, testing)

Footnotes on the libfuncs list:

  1. It is implemented but we're not handling potential issues like lifetimes yet.
  2. It is implemented but we're still debating whether it should be a Rust-like Box<T> or if it's fine treating it like another variable.
  3. It is implemented but side-effects are not yet handled (ex. array cloning/dropping).
  4. Not supported by the Cairo to Sierra compiler.
  5. Implemented with a dummy. It doesn't do anything yet.

Getting Started

Dependencies

  • Linux or macOS (aarch64 included) only for now
  • LLVM 17 with MLIR: On debian you can use apt.llvm.org, on macOS you can use brew
  • Nightly Rust
  • Git

Setup

This step applies to all operating systems.

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

make deps

Linux

Since Linux distributions change widely, you need to install LLVM 17 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:

sudo apt-get install llvm-17 llvm-17-dev llvm-17-runtime clang-17 clang-tools-17 lld-17 libpolly-17-dev libmlir-17-dev mlir-17-tools

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

Install LLVM from source instructions
# Go to https://github.com/llvm/llvm-project/releases
# Download the latest LLVM 17 release:
# The blob to download is called llvm-project-17.x.x.src.tar.xz

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

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

# The following cmake command configures the build to be installed to /opt/llvm-17
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-17 \
   -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

Setup a environment variable called MLIR_SYS_170_PREFIX, LLVM_SYS_170_PREFIX and TABLEGEN_170_PREFIX pointing to the llvm directory:

# For Debian/Ubuntu using the repository, the path will be /usr/lib/llvm-17
export MLIR_SYS_170_PREFIX=/usr/lib/llvm-17
export LLVM_SYS_170_PREFIX=/usr/lib/llvm-17
export TABLEGEN_170_PREFIX=/usr/lib/llvm-17

Run the deps target to install the other dependencies such as the cairo compiler (for tests, benchmarks).

make deps

MacOS

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

source env-macos.sh

Make commands:

Running make by itself will list available targets.

  • Install the necessary dependencies (on Linux, you need to get LLVM 17 manually):
make deps
  • Build a release version:
make build

Or with your native CPU Architecture for even more performance (usually):

make build-native
  • Install the cairo-native-dump and cairo-native-run commands:
make install
  • Build a optimized development version:
make build-dev
  • View and open the docs:
make doc-open
  • Run the tests:
make test
  • Generate coverage:
make coverage
  • Run clippy and format checks:
make check
  • Compile the runtime library used for ahead of time compilation:
make runtime

Command Line Interface

cairo-native-dump:

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

Arguments:
  <INPUT>

Options:
  -o, --output <OUTPUT>  [default: -]
  -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.

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

Usage: cairo-native-run [OPTIONS] <INPUT> <ENTRY_POINT>

Arguments:
  <INPUT>
  <ENTRY_POINT>

Options:
  -i, --inputs <INPUTS>
  -o, --outputs <OUTPUTS>
  -p, --print-outputs
  -h, --help               Print help

API usage example

This is a usage example using the API for an easy Cairo program that requires the least setup to get running. It allows you to compile and execute a program using the JIT.

Example code to run a program:

use starknet_types_core::felt::Felt;
use cairo_native::context::NativeContext;
use cairo_native::executor::NativeExecutor;
use cairo_native::values::JitValue;
use std::path::Path;

fn main() {
    let program_path = Path::new("programs/examples/hello.cairo");
    // Compile the cairo program to sierra.
    let sierra_program = cairo_native::utils::cairo_to_sierra(program_path);

    // Instantiate a Cairo Native MLIR context. This data structure is responsible for the MLIR
    // initialization and compilation of sierra programs into a MLIR module.
    let native_context = NativeContext::new();

    // Compile the sierra program into a MLIR module.
    let native_program = native_context.compile(&sierra_program).unwrap();

    // The parameters of the entry point.
    let params = &[JitValue::Felt252(Felt::from_bytes_be_slice(b"user"))];

    // Find the entry point id by its name.
    let entry_point = "hello::hello::greet";
    let entry_point_id = cairo_native::utils::find_function_id(&sierra_program, entry_point);

    // Instantiate the executor.
    let native_executor = NativeExecutor::new(native_program);

    // Execute the program.
    let result = native_executor
        .execute(entry_point_id, params, None)
        .unwrap();

    println!("Cairo program was compiled and executed successfully.");
    println!("{:?}", result);
}

Example code to run a Starknet contract:

use starknet_types_core::felt::Felt;
use cairo_lang_compiler::CompilerConfig;
use cairo_lang_starknet::contract_class::compile_path;
use cairo_native::context::NativeContext;
use cairo_native::executor::NativeExecutor;
use cairo_native::utils::find_entry_point_by_idx;
use cairo_native::values::JitValue;
use cairo_native::{
    metadata::syscall_handler::SyscallHandlerMeta,
    starknet::{BlockInfo, ExecutionInfo, StarkNetSyscallHandler, SyscallResult, TxInfo, U256},
};
use std::path::Path;

/// To run a starknet contract, we need to use a syscall handler, here we show how to implement one (at the end).
#[derive(Debug)]
struct SyscallHandler;

fn main() {
    let path = Path::new("programs/examples/hello_starknet.cairo");

    let contract = compile_path(
        path,
        None,
        CompilerConfig {
            replace_ids: true,
            ..Default::default()
        },
    )
    .unwrap();

    let entry_point = contract.entry_points_by_type.constructor.get(0).unwrap();
    let sierra_program = contract.extract_sierra_program().unwrap();

    let native_context = NativeContext::new();

    let mut native_program = native_context.compile(&sierra_program).unwrap();
    native_program
        .insert_metadata(SyscallHandlerMeta::new(&mut SyscallHandler))
        .unwrap();

    // Call the echo function from the contract using the generated wrapper.
    let entry_point_fn =
        find_entry_point_by_idx(&sierra_program, entry_point.function_idx).unwrap();

    let fn_id = &entry_point_fn.id;

    let native_executor = NativeExecutor::new(native_program);

    let result = native_executor
        .execute_contract(
            fn_id,
            // The calldata
            &[JitValue::Felt252(Felt::from(1))],
            u64::MAX.into(),
        )
        .expect("failed to execute the given contract");

    println!();
    println!("Cairo program was compiled and executed successfully.");
    println!("{result:#?}");
}

// Implement an example syscall handler.
impl StarkNetSyscallHandler for SyscallHandler {
    fn get_block_hash(
        &mut self,
        block_number: u64,
        _gas: &mut u128,
    ) -> SyscallResult<Felt> {
        println!("Called `get_block_hash({block_number})` from MLIR.");
        Ok(Felt::from_bytes_be_slice(b"get_block_hash ok"))
    }

    fn get_execution_info(
        &mut self,
        _gas: &mut u128,
    ) -> SyscallResult<cairo_native::starknet::ExecutionInfo> {
        println!("Called `get_execution_info()` from MLIR.");
        Ok(ExecutionInfo {
            block_info: BlockInfo {
                block_number: 1234,
                block_timestamp: 2345,
                sequencer_address: 3456.into(),
            },
            tx_info: TxInfo {
                version: 4567.into(),
                account_contract_address: 5678.into(),
                max_fee: 6789,
                signature: vec![1248.into(), 2486.into()],
                transaction_hash: 9876.into(),
                chain_id: 8765.into(),
                nonce: 7654.into(),
            },
            caller_address: 6543.into(),
            contract_address: 5432.into(),
            entry_point_selector: 4321.into(),
        })
    }

    fn deploy(
        &mut self,
        class_hash: Felt,
        contract_address_salt: Felt,
        calldata: &[Felt],
        deploy_from_zero: bool,
        _gas: &mut u128,
    ) -> SyscallResult<(Felt, Vec<Felt>)> {
        println!("Called `deploy({class_hash}, {contract_address_salt}, {calldata:?}, {deploy_from_zero})` from MLIR.");
        Ok((
            class_hash + contract_address_salt,
            calldata.iter().map(|x| x + &Felt::from(1)).collect(),
        ))
    }

    fn replace_class(
        &mut self,
        class_hash: Felt,
        _gas: &mut u128,
    ) -> SyscallResult<()> {
        println!("Called `replace_class({class_hash})` from MLIR.");
        Ok(())
    }

    fn library_call(
        &mut self,
        class_hash: Felt,
        function_selector: Felt,
        calldata: &[Felt],
        _gas: &mut u128,
    ) -> SyscallResult<Vec<Felt>> {
        println!(
            "Called `library_call({class_hash}, {function_selector}, {calldata:?})` from MLIR."
        );
        Ok(calldata.iter().map(|x| x * Felt::from(3)).collect())
    }

    fn call_contract(
        &mut self,
        address: Felt,
        entry_point_selector: Felt,
        calldata: &[Felt],
        _gas: &mut u128,
    ) -> SyscallResult<Vec<Felt>> {
        println!(
            "Called `call_contract({address}, {entry_point_selector}, {calldata:?})` from MLIR."
        );
        Ok(calldata.iter().map(|x| x * Felt::from(3)).collect())
    }

    fn storage_read(
        &mut self,
        address_domain: u32,
        address: Felt,
        _gas: &mut u128,
    ) -> SyscallResult<Felt> {
        println!("Called `storage_read({address_domain}, {address})` from MLIR.");
        Ok(address * Felt::from(3))
    }

    fn storage_write(
        &mut self,
        address_domain: u32,
        address: Felt,
        value: Felt,
        _gas: &mut u128,
    ) -> SyscallResult<()> {
        println!("Called `storage_write({address_domain}, {address}, {value})` from MLIR.");
        Ok(())
    }

    fn emit_event(
        &mut self,
        keys: &[Felt],
        data: &[Felt],
        _gas: &mut u128,
    ) -> SyscallResult<()> {
        println!("Called `emit_event({keys:?}, {data:?})` from MLIR.");
        Ok(())
    }

    fn send_message_to_l1(
        &mut self,
        to_address: Felt,
        payload: &[Felt],
        _gas: &mut u128,
    ) -> SyscallResult<()> {
        println!("Called `send_message_to_l1({to_address}, {payload:?})` from MLIR.");
        Ok(())
    }

    fn keccak(
        &mut self,
        input: &[u64],
        _gas: &mut u128,
    ) -> SyscallResult<cairo_native::starknet::U256> {
        println!("Called `keccak({input:?})` from MLIR.");
        Ok(U256(Felt::from(1234567890).to_le_bytes()))
    }

    /*
    ... more code here, check out the full example in examples/starknet.rsd
    */
}

For more examples, check out the examples/ directory.

Benchmarking

Requirements

You need to setup some environment variables:

$MLIR_SYS_170_PREFIX=/path/to/llvm17  # Required for non-standard LLVM install locations.
$LLVM_SYS_170_PREFIX=/path/to/llvm17  # Required for non-standard LLVM install locations.
$TABLEGEN_170_PREFIX=/path/to/llvm17  # Required for non-standard LLVM install locations.
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.

From MLIR to native binary

# to mlir with llvm dialect
sierra2mlir program.sierra -o program.mlir

# translate all dialects to the llvm dialect
"$MLIR_SYS_170_PREFIX/bin/mlir-opt" \
        --canonicalize \
        --convert-scf-to-cf \
        --canonicalize \
        --cse \
        --expand-strided-metadata \
        --finalize-memref-to-llvm \
        --convert-func-to-llvm \
        --convert-index-to-llvm \
        --reconcile-unrealized-casts \
        "program.mlir" \
        -o "program-llvm.mlir"

# translate mlir to llvm-ir
"$MLIR_SYS_170_PREFIX"/bin/mlir-translate --mlir-to-llvmir program-llvm.mlir -o program.ll

# compile natively
"$MLIR_SYS_170_PREFIX"/bin/clang program.ll -Wno-override-module \
    -L "$MLIR_SYS_170_PREFIX"/lib -L"./target/release/" \
    -lsierra2mlir_utils -lmlir_c_runner_utils \
    -Wl,-rpath "$MLIR_SYS_170_PREFIX"/lib \
    -Wl,-rpath ./target/release/ \
    -o program

./program

cairo-native-test cli tool

This tool mimics the cairo-test tool and is identical to it, the only feature it doesn't have is the profiler.

You can download it on our releases page.

$ cairo-native-test --help
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.

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's People

Contributors

azteca1998 avatar clementwalter avatar domhenderson avatar edg-l avatar entropidelic avatar fmoletta avatar gerson2102 avatar greged93 avatar hhamud avatar igaray avatar jordibonet-lambdaclass avatar jrchatruc avatar klaus993 avatar pablodeymo avatar pefontana avatar rodrigo-pino avatar toni-calvin avatar unbalancedparentheses avatar wugalde19 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

cairo_native's Issues

Proc macro utility crate to convert LLVM/MLIR IR to the C API equivalent code

The purpose of this issue is to provide a way to implement MLIR stuff without the boilerplate associated with the C API, or melior in our case.

This macro will be especially useful when implementing large portions of code, as is the case with felt divison and the print implementations.

For starters, it only needs to parse the MLIR code and generate its representation, which can then be transferred into the appropiate module.

Usage example

impl<'ctx> Compiler<'ctx> {
    pub fn create_libfunc_print(
        &'ctx self,
        func_decl: &LibfuncDeclaration,
        parent_block: BlockRef<'ctx>,
        storage: &mut Storage<'ctx>,
    ) -> Result<()> {
        let module = mlir_asm! { &self.context =>
            func.func @print(%arg : llvm.struct<(i32, i32, !llvm.ptr)>) -> () {
                // ...
            }
        };

        // Transfer to our root module.
        {
            fn push_recursive(m: BlockRef, op: OperationRef) {
                m.append_operation();
                if let Some(next_op) = op.next_in_block() {
                    push_recursive(m, next_op);
                }
            }

            let module_body = module.body();
            push_recursive(parent_block, module_body.first_operation().unwrap());
        }

        storage.libfuncs.insert(
            func_decl.id.debug_name.as_deref().unwrap().to_string(),
            SierraLibFunc::create_function_all_args(vec![...], vec![]),
        );

        Ok(())
    }
}

Set up a iai CI benchmark pipeline

Current benchmarks are run either with external scripts, or with criterion for microbenchmarks.
We should research incorporaring Iai into the benchmarks run as part of our CI pipeline so that they can be used not as proper performance regression tests.

Libfuncs: uint

  • Sierra implementation for 8,16,32 and 64 bit widths
  • Sierra implementation for 128 bit width
  • Libfuncs for a given width:
    • uN_const
    • uN_eq uN_eq(T, T) -> [branch, branch] (fallthrough 0)
    • uN_is_zero uN_is_zero(T) -> [branch, branch] (fallthrough 0)
    • uN_le uN_le(RangeCheckPtr, T, T) -> [branch, branch] (fallthrough 0)
    • uN_lt uN_lt(RangeCheckPtr, T, T) -> [branch, branch] (fallthrough 0)
    • uN_overflowing_add _overflowing_add(RangeCheckPtr, T, T) -> [branch(RangeCheckPtr, T), branch(RangeCheckPtr, T)]
    • uN_overflowing_sub _overflowing_sub(RangeCheckPtr, T, T) -> [branch(RangeCheckPtr, T), branch(RangeCheckPtr, T)]
    • uN_safe_divmod
    • uN_to_felt252
    • uN_try_from_felt252
    • uN_wide_mul

Tracking issue: starknet interoperability libfuncs

Some of the following libfuncs may warrant their own issue:

  • Sierra implementation
    • contract_address_const
    • contract_address_to_felt252
    • contract_address_try_from_felt252
    • class_hash_const
    • class_hash_to_felt252
    • class_hash_try_from_felt252
    • call_contract_syscall
    • deploy_syscall
    • library_call_syscall
    • send_message_to_l1_syscall

Print

Implement functionality to be able to print Felt252's in the emitted programs, for debugging aid.
This is distinct from the print libfunc which supports the print facility in cairo.
Additionally, see how the debugging tips can be applied from a rust context

Libfunc: uintN sqrt

The sqrt libfunc is marked experimental and does not seem to be very used.

Fix jump and felt_is_zero

When there are 2 paths, and the point where they join uses a variable computed within those paths, it currently errors because it doesn't dominate it's use. To fix this we need to pass the variables through the block arguments.

Improv Makefile test target

  • Add a Makefile pattern rule to build the examples.
  • Remove and exclude examples/*.sierra, examples/*.mlir and examples/*.ll from git.

Libfuncs: consts

Sierra implementation

The consts module in cairo-sierra-lang does not contain a particular libfunc but instead the ConstGenLibfunc trait, WrapConstGenLibfunc and SignatureAndConstConcreteLibfunc types. Explore how these are used in sierra and whether they need to be taken into account when translating sierra to MLIR.

Libfunc: gas

  • Compiler internals:
    • Gas global variable
    • Gas global variable manipulation (increase, decrease, get)
  • Libfuncs:
    • withdraw_gas
    • withdraw_gas_all
    • get_available_gas
    • get_builtin_costs

Some documentation references:

Note: Cairo prevents us from running program with infinite loops by including a gas meter. The gas meter is a mechanism that limits the amount of computation that can be done in a program. By setting a value to the --available-gas flag, we can set the maximum amount of gas available to the program. Gas is a unit of measurementsthat expresses the computation cost of an instruction. When the gas meter runs out, the program will stop. In this case, the program panicked because it ran out of gas, as the stop condition was never reached. It is particularly important in the context of smart contracts deployed on Starknet, as it prevents from running infinite loops on the network. If you're writing a program that needs to run a loop, you will need to execute it with the --available-gas flag set to a value that is large enough to run the program.

As recursive functions can be a source of infinite loops if the stop condition is never met, the Cairo-to-Sierra compiler will inject a withdraw_gas method at the beginning of recursive functions. As this feature has not yet been implemented, developers are still expected to call withdraw_gas in recursive functions and handle the result themselves, although it should be included in a future release of the compiler.
This withdraw_gas function will deduct the amount of gas required to run the function from the total gas available for the transaction by evaluating the cost of running each instruction in the function. The cost is analyzed by determining how many steps each operation takes - which is known at compile time for most operations. During the execution of a Cairo program, if the withdraw_gas call returns a null or negative value, the current function execution will stop, all pending variables will be consumed by calling dict_squash on unsquashed dictionaries and calling drop on other variables, and the execution will be considered as failed. Because all transactions on Starknet have a limited amount of spendable gas to execute the transactions, infinite loops are avoided - and by ensuring that enough gas is still available to drop the variables and stop the execution, the sequencer will be able to collect the fees from the transaction failure.

The branch_align libfunc is used to equalize gas costs and ap changes across merging paths of branching code.

Code:

External code invocation

It will eventually be necessary to call functions implemented in rust from the emitted programs.
Research which mechanism allows this, and lay down the framework needed.

Property test support

Include proptest as a dependency, examine the codebase for possible areas to property test and create separate issues for them.

check and fix u128 dprintf

The dprintf call for u128 values most likely isn't working due to size constraints, possible fix: print the upper 64bits in hex and then the lower 64bits

Debugger support

Examine MLIR's and melior's ability to add support for running generated programs under lldb with DWARF information.

Libfunc: felt252_dict

Cairo 1 also supports dictionaries in the core library. A dictionary is a data structure that stores key-value pairs. The key is used to look up a specific value, much like looking up a word in a print dictionary. It is currently possible to create dictionaries that map felts to u128, felt, u8, u64 and Nullable::.

Because short strings are actually felts, it is possible to create string keys in a dictionary.

example:

use dict::Felt252DictTrait;

fn main() -> u32 {
    let mut dict: Felt252Dict<u32> = Felt252DictTrait::new();
    dict.insert('hello', 2_u32);
    let value = dict.get('hello');
    value
}

let mut d = DictFeltToTrait::new();
d.insert(1, 1); // Write.
d.get(1); // Read.
d.squash(); // Destroy the dictionary.

Fix `--main-print` and `--print-fd` flags combination

The --print-fd flag works only when printing the return value of main (that is, when combined with --main-print). It should also work when using the print trait.

Negative --print-fd values should disable printing, however right now dprintf() is called with the negative file descriptor, which causes it to fail. The effect is the same, but the implementation is incorrect and should be fixed.

Libfunc: storage

Sierra implementation

  • storage_base_address_const
  • storage_address_to_felt252
  • storage_address_from_base
  • storage_address_from_base_and_offset
  • storage_address_try_from_felt252
  • storage_base_address_from_felt252
  • storage_read_syscall
  • storage_write_syscall

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.