Skip to content

Commit

Permalink
Combined AOT + arguments by memory. (#390)
Browse files Browse the repository at this point in the history
* add ffi to translate to llvm ir

* better

* progress

* module to object

* ld

* compile

* add compile

* clean

* fix ci

* fix macos

* progress

* progress

* progress

* try

* investigate

* needed 1 more argument

* changes

* weird

* realloc

* clean a bit

* cleanup

* AOT preview.

* Fix ARM trampoline.

* Fix stuff.

* Fix support for any number of arguments (stack alignment).

* Prepare for x86_64 support.

* Make it work on x86_64 linux.

* Document x86_64 trampoline assembly.

* Add call arguments flattening.

* Updates.

* Fix return values in `aarch64`.

* Add AOT versions of NativeExecutor and ProgramCache.

* Rename `JITValue` to `JitValue` to comply with Rust's naming convention.

* Add enum ABI testing.

* Fix docs.

* Handle enum arguments.

* Refactor enums into the stack.

* Minor fix.

* Make structures optionally memory-allocated.

* Fix tail recursion with memory-allocated arguments.

* Fix JIT value deserialization when memory allocated.

* Fix typo.

* Fix bool libfuncs.

* Add `print_i8` debug utillity.

* Fix invocation arguments.

* Register debug utils on integration tests.

* Fix memory-allocated enum payloads.

* Fix JitValue deserialization.

* Add `print_i128` debug utility.

* Fix JIT invoke return pointer offsets.

* Fix stuff.

* Merge `src/jit_runtime.rs` into `src/executor/jit.rs`. Unify APIs. Various fixes.

* Fix stuff.

* It seems to be working now.

* Support stack-allocated C-style enums.

* Remove unused file.

* Remove builtins from function signatures. Fix `enum_init`  for non-memory-allocated enums.

* Fix boolean libfuncs (bools are not memory-allocated).

* Implement multi-variant non-memory-allocated enum support.

* Support non-memory-allocated enum matches.

* Fix gas handling.

* Fix `enum_match` libfunc for C-style and single-variant non-memory-allocated enums.

* Add support for `sint8`, `sint16` and `sint32`.

* Lots of fixes.

* Reorganize code for DRY. Support Starknet contracts.

* Support `EcPoint`, `EcState` and `NonZero<T>` return types.

* Support the syscall handler when calling `invoke_dynamic` directly.

* Make contracts accept only felts and convert to `Span<felt252>` internally. Support snapshot arguments. Various fixes.

* Fix gas on integration tests.

* Minor fix.

* Fix non-syscall-handler Starknet types.

* Support dictionaries. Minor fixes.

* Fix non-syscall-handler Starknet types support.

* Minor fix.

* Fix linker.

* Merge branch 'main' into aot-with-stack-enums

* Remove irrelevant autogenerated files.

* Remove calling convention discovery testing code.

* Revert "Remove irrelevant autogenerated files.".
Reason: those files weren't autogenerated by my testing.

This reverts commit 53f65ed.

* Undo conditional benchmark execution.

* Fix formatting and clippy issues.

* Remove unsafe transmute that is no longer necessary.

* Sort dependencies alphabetically.

* Implement `Debug` for the contract caches.

* Refactor out the necessary refcell.

* Actually remove the refcell.

* Fix stuff from previous changes.

* Fix stuff.

* Fix warnings.

* Fix errors.

* Fix argument stack align.

* Fix warnings.

* use env var to add aditional dir to search for runtime lib (#400)

* use env var to add aditional dir to search for runtime lib

* oops

* Ignore failing tests.

* Rename env var to something that makes more sense. Set the variable on the CI runners.

* Fix macOS and coverage CI runs.

* Fix CI again.

* Remove debug utils commented code.

* Fix merge errors.

---------

Co-authored-by: Edgar Luque <[email protected]>
  • Loading branch information
azteca1998 and edg-l authored Dec 21, 2023
1 parent 2e861d6 commit 6b244ff
Show file tree
Hide file tree
Showing 84 changed files with 4,979 additions and 2,583 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ jobs:
name: test (linux, amd64)
runs-on: ubuntu-latest
env:
CAIRO_NATIVE_RUNTIME_LIBDIR: ${{ github.workspace }}/target/optimized-dev
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
MLIR_SYS_170_PREFIX: /usr/lib/llvm-17/
LLVM_SYS_170_PREFIX: /usr/lib/llvm-17/
Expand Down Expand Up @@ -116,12 +117,15 @@ jobs:
run: 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
- name: Install deps
run: make deps
- name: Build cairo-native-runtime library.
run: cargo build --profile=optimized-dev --package=cairo-native-runtime
- name: test
run: make test
test_macos:
name: Test (macOS, Apple silicon)
runs-on: [self-hosted, macOS]
env:
CAIRO_NATIVE_RUNTIME_LIBDIR: ${{ github.workspace }}/target/optimized-dev
CARGO_TERM_COLOR: always
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
MLIR_SYS_170_PREFIX: /opt/homebrew/opt/llvm@17
Expand All @@ -134,18 +138,21 @@ jobs:
uses: dtolnay/[email protected]
with:
components: clippy
- name: Install scarb
- name: Install scarb
uses: software-mansion/setup-scarb@v1
with:
scarb-version: "2.4.0"
- name: Install deps
run: make deps
- name: Build cairo-native-runtime library.
run: cargo build --profile=optimized-dev --package=cairo-native-runtime
- name: Run tests
run: make test
coverage:
name: coverage
runs-on: [self-hosted, macOS]
env:
CAIRO_NATIVE_RUNTIME_LIBDIR: ${{ github.workspace }}/target/optimized-dev
CARGO_TERM_COLOR: always
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
MLIR_SYS_170_PREFIX: /opt/homebrew/opt/llvm@17
Expand All @@ -160,12 +167,14 @@ jobs:
uses: Swatinem/rust-cache@v2
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Install scarb
- name: Install scarb
uses: software-mansion/setup-scarb@v1
with:
scarb-version: "2.4.0"
- name: Install deps
run: make deps
- name: Build cairo-native-runtime library.
run: cargo build --profile=optimized-dev --package=cairo-native-runtime
- name: test and generate coverage
run: make coverage
- name: Upload coverage to Codecov
Expand Down
19 changes: 19 additions & 0 deletions AOT-CallingConvention.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# AOT calling convention:

## Arguments

- Written on registers, then the stack.
- Structs' fields are treated as individual arguments (flattened).
- Enums are structs internally, therefore they are also flattened (including the padding).
- The default payload works as expected since it has the correct signature.
- All other payloads require breaking it down into bytes and scattering it through the padding
and default payload's space.

## Return values

- Indivisible values that do not fit within a single register (ex. felt252) use multiple registers
(x0-x3 for felt252).
- Struct arguments, etc... use the stack.

In other words, complex values require a return pointer while simple values do not but may still use
multiple registers if they don't fit within one.
25 changes: 18 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 10 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,37 +39,36 @@ id-arena = "2.2"
itertools = "0.12"
lazy_static = "1.4"
libc = "0.2.147"
melior = { version = "0.14.0", features = ["ods-dialects"] }
llvm-sys = "170.0.0"
melior = { version = "0.14.1", features = ["ods-dialects"] }
mlir-sys = "0.2.1"
num-bigint = "0.4.4"
num-traits = "0.2"
starknet-types-core = { git = "https://github.com/starknet-io/types-rs.git", rev = "4bc8a5db3c86f017e8be7dc43531a2cb6db011c0", default-features = false, features = ["serde"] }
tempfile = "3.6"
thiserror = "1.0"
tracing = "0.1"
llvm-sys = "170.0.0"
tempfile = "3.6"
starknet-types-core = { git = "https://github.com/starknet-io/types-rs.git", rev = "4bc8a5db3c86f017e8be7dc43531a2cb6db011c0", default-features = false, features = ["serde"] }


# CLI dependencies
cairo-lang-sierra-ap-change = "2.3.1"
cairo-lang-sierra-gas = "2.3.1"
cairo-lang-starknet = "2.3.1"
cairo-lang-utils = "2.3.1"
cairo-native-runtime = { path = "runtime", optional = true }
clap = { version = "4.3", features = ["derive"], optional = true }
libloading = "0.8.1"
tracing-subscriber = { version = "0.3", features = [
"env-filter",
], optional = true }
cairo-lang-sierra-gas = "2.3.1"
cairo-lang-sierra-ap-change = "2.3.1"
cairo-lang-utils = "2.3.1"
cairo-lang-starknet = "2.3.1"

[dev-dependencies]
cairo-lang-runner = "2.3.1"
cairo-felt = "0.8.5"
cairo-lang-runner = "2.3.1"
criterion = { version = "0.5.1", features = ["html_reports"] }
lambdaworks-math = "0.1"
num-traits = "0.2"
pretty_assertions_sorted = "1.2.3"
proptest = "1.2"
tempfile = "3.6"
test-case = "3.2.1"
walkdir = "2"
serde_json = { version = "1.0"}
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ 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 cairo_native::values::JitValue;
use std::path::Path;

fn main() {
Expand All @@ -445,7 +445,7 @@ fn main() {
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"))];
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";
Expand Down Expand Up @@ -473,7 +473,7 @@ 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::values::JitValue;
use cairo_native::{
metadata::syscall_handler::SyscallHandlerMeta,
starknet::{BlockInfo, ExecutionInfo, StarkNetSyscallHandler, SyscallResult, TxInfo, U256},
Expand Down Expand Up @@ -519,7 +519,7 @@ fn main() {
.execute_contract(
fn_id,
// The calldata
&[JITValue::Felt252(Felt::from(1))],
&[JitValue::Felt252(Felt::from(1))],
u64::MAX.into(),
)
.expect("failed to execute the given contract");
Expand Down
1 change: 1 addition & 0 deletions benches/compile_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,5 @@ pub fn bench_compile_time(c: &mut Criterion) {
}

criterion_group!(benches, bench_compile_time);

criterion_main!(benches);
12 changes: 6 additions & 6 deletions benches/libfuncs.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use cairo_lang_runner::StarknetState;
use cairo_native::{context::NativeContext, executor::NativeExecutor};
use cairo_native::{context::NativeContext, executor::JitNativeExecutor};
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use util::{create_vm_runner, prepare_programs};

Expand Down Expand Up @@ -56,11 +56,11 @@ pub fn bench_libfuncs(c: &mut Criterion) {
b.iter(|| {
let module = native_context.compile(program).unwrap();
// pass manager internally verifies the MLIR output is correct.
let native_executor = NativeExecutor::new(module);
let native_executor = JitNativeExecutor::new(module);

// Execute the program.
let result = native_executor
.execute(&entry.id, &[], Some(u64::MAX as u128))
.invoke_dynamic(&entry.id, &[], Some(u64::MAX as u128), None)
.unwrap();
black_box(result)
})
Expand All @@ -74,19 +74,19 @@ pub fn bench_libfuncs(c: &mut Criterion) {
let native_context = NativeContext::new();
let module = native_context.compile(program).unwrap();
// pass manager internally verifies the MLIR output is correct.
let native_executor = NativeExecutor::new(module);
let native_executor = JitNativeExecutor::new(module);

// warmup
for _ in 0..5 {
native_executor
.execute(&entry.id, &[], Some(u64::MAX as u128))
.invoke_dynamic(&entry.id, &[], Some(u64::MAX as u128), None)
.unwrap();
}

b.iter(|| {
// Execute the program.
let result = native_executor
.execute(&entry.id, &[], Some(u64::MAX as u128))
.invoke_dynamic(&entry.id, &[], Some(u64::MAX as u128), None)
.unwrap();
black_box(result)
})
Expand Down
3 changes: 1 addition & 2 deletions benches/util.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::sync::Arc;

use cairo_lang_runner::SierraCasmRunner;
use cairo_lang_sierra::program::Program;
use std::sync::Arc;
use walkdir::WalkDir;

pub fn prepare_programs(path: &str) -> Vec<(Arc<Program>, String)> {
Expand Down
10 changes: 5 additions & 5 deletions examples/easy_api.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cairo_native::context::NativeContext;
use cairo_native::executor::NativeExecutor;
use cairo_native::values::JITValue;
use cairo_native::executor::JitNativeExecutor;
use cairo_native::values::JitValue;
use starknet_types_core::felt::Felt;
use std::path::Path;

Expand All @@ -17,18 +17,18 @@ fn main() {
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"))];
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);
let native_executor = JitNativeExecutor::new(native_program);

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

println!("Cairo program was compiled and executed successfully.");
Expand Down
27 changes: 12 additions & 15 deletions examples/erc20.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
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::executor::JitNativeExecutor;
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},
Expand Down Expand Up @@ -308,29 +307,27 @@ fn main() {

let native_context = NativeContext::new();

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

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 native_executor = JitNativeExecutor::new(native_program);

let result = native_executor
.execute_contract(
.invoke_contract_dynamic(
fn_id,
&[
JITValue::Felt252(Felt::from_bytes_be_slice(b"name")),
JITValue::Felt252(Felt::from_bytes_be_slice(b"symbol")),
JITValue::Felt252(Felt::from(0)),
JITValue::Felt252(Felt::from(i64::MAX)),
JITValue::Felt252(Felt::from(4)),
JITValue::Felt252(Felt::from(6)),
Felt::from_bytes_be_slice(b"name"),
Felt::from_bytes_be_slice(b"symbol"),
Felt::from(0),
Felt::from(i64::MAX),
Felt::from(4),
Felt::from(6),
],
u64::MAX.into(),
Some(u128::MAX),
Some(&SyscallHandlerMeta::new(&mut SyscallHandler)),
)
.expect("failed to execute the given contract");

Expand Down
8 changes: 4 additions & 4 deletions examples/invoke.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cairo_native::executor::NativeExecutor;
use cairo_native::executor::JitNativeExecutor;
use cairo_native::utils::find_entry_point;
use cairo_native::{context::NativeContext, values::JITValue};
use cairo_native::{context::NativeContext, values::JitValue};
use std::path::Path;
use tracing_subscriber::{EnvFilter, FmtSubscriber};

Expand Down Expand Up @@ -28,9 +28,9 @@ fn main() {

let fn_id = &entry_point_fn.id;

let native_executor = NativeExecutor::new(native_program);
let native_executor = JitNativeExecutor::new(native_program);

let output = native_executor.execute(fn_id, &[JITValue::Felt252(1.into())], None);
let output = native_executor.invoke_dynamic(fn_id, &[JitValue::Felt252(1.into())], None, None);

println!();
println!("Cairo program was compiled and executed successfully.");
Expand Down
Loading

0 comments on commit 6b244ff

Please sign in to comment.