From 7c4ff7f64e81bd0c0dd325a359b36d765d5dc2ac Mon Sep 17 00:00:00 2001 From: Edgar Date: Wed, 16 Oct 2024 14:28:33 +0200 Subject: [PATCH 1/6] Better function attributes and re-enable >O1 opt (#843) * Fix felt252 and enum deserialization bugs. * Fix formatting. * Also fix the runtime. * Fix errors. * try to fix ci * remove unused deps * proper function attributes * add proper function attrs to optimize better, add some passes, run tests with atleast some opts * dont use remi * oops * maybe with opt level 3 now it works * test * works * readd deleted bench * remove dbg * Update bench-hyperfine.sh * fixci * comment * Update src/ffi.rs Co-authored-by: MrAzteca --------- Co-authored-by: Esteve Soler Arderiu Co-authored-by: Esteve Soler Arderiu Co-authored-by: MrAzteca --- Makefile | 3 +- src/compiler.rs | 30 ++-- src/ffi.rs | 9 +- src/libfuncs/function_call.rs | 10 +- src/metadata/drop_overrides.rs | 17 ++- src/metadata/dup_overrides.rs | 17 ++- src/metadata/runtime_bindings.rs | 226 ++++++++++++++++++++++--------- src/types/felt252_dict.rs | 24 +++- src/utils.rs | 3 +- 9 files changed, 247 insertions(+), 92 deletions(-) diff --git a/Makefile b/Makefile index 5d018b1f4..217091874 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,8 @@ doc-open: check-llvm cargo doc --all-features --no-deps --workspace --open .PHONY: bench -bench: build needs-cairo2 runtime +bench: needs-cairo2 runtime + cargo b --release --bin cairo-native-run ./scripts/bench-hyperfine.sh .PHONY: bench-ci diff --git a/src/compiler.rs b/src/compiler.rs index 9acd14c93..85c4e2059 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -922,12 +922,16 @@ fn compile_func( &[ ( Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "public").into(), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ( + Identifier::new(context, "CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), ), - // ( - // Identifier::new(context, "CConv"), - // Attribute::parse(context, "#llvm.cconv").unwrap(), - // ), ], Location::fused( context, @@ -1351,10 +1355,10 @@ fn generate_entry_point_wrapper<'c>( Identifier::new(context, "callee"), FlatSymbolRefAttribute::new(context, private_symbol).into(), ), - // ( - // Identifier::new(context, "CConv"), - // Attribute::parse(context, "#llvm.cconv").unwrap(), - // ), + ( + Identifier::new(context, "CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), + ), ]) .add_operands(&args) .add_results(&[llvm::r#type::r#struct(context, ret_types, false)]) @@ -1385,6 +1389,14 @@ fn generate_entry_point_wrapper<'c>( Identifier::new(context, "sym_visibility"), StringAttribute::new(context, "public").into(), ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ( + Identifier::new(context, "llvm.CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), + ), ( Identifier::new(context, "llvm.emit_c_interface"), Attribute::unit(context), diff --git a/src/ffi.rs b/src/ffi.rs index 0e6b563ce..1d1a7f412 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -143,7 +143,7 @@ pub fn module_to_object(module: &Module<'_>, opt_level: OptLevel) -> Result LLVMCodeGenOptLevel::LLVMCodeGenLevelDefault, OptLevel::Aggressive => LLVMCodeGenOptLevel::LLVMCodeGenLevelAggressive, }, - LLVMRelocMode::LLVMRelocDynamicNoPic, + LLVMRelocMode::LLVMRelocPIC, LLVMCodeModel::LLVMCodeModelDefault, ); @@ -152,8 +152,11 @@ pub fn module_to_object(module: &Module<'_>, opt_level: OptLevel) -> Result 0, OptLevel::Less => 1, - OptLevel::Default => 1, // todo: change once slp-vectorizer pass is fixed on llvm - OptLevel::Aggressive => 1, // https://github.com/llvm/llvm-project/issues/107198 + // slp-vectorizer pass did cause some issues, but after the change + // on function attributes it seems to not trigger them anymore. + // https://github.com/llvm/llvm-project/issues/107198 + OptLevel::Default => 2, + OptLevel::Aggressive => 3, }; let passes = CString::new(format!("default")).unwrap(); let error = LLVMRunPasses(llvm_module, passes.as_ptr(), machine, opts); diff --git a/src/libfuncs/function_call.rs b/src/libfuncs/function_call.rs index 4594219ad..b3b80d4a3 100644 --- a/src/libfuncs/function_call.rs +++ b/src/libfuncs/function_call.rs @@ -23,7 +23,7 @@ use melior::{ attribute::{DenseI32ArrayAttribute, FlatSymbolRefAttribute}, operation::OperationBuilder, r#type::IntegerType, - Block, Identifier, Location, Type, Value, + Attribute, Block, Identifier, Location, Type, Value, }, Context, }; @@ -195,10 +195,10 @@ pub fn build<'ctx, 'this>( ) .into(), ), - // ( - // Identifier::new(context, "CConv"), - // Attribute::parse(context, "#llvm.cconv").unwrap(), - // ), + ( + Identifier::new(context, "CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), + ), ]) .add_operands(&arguments) .add_results(&[llvm::r#type::r#struct(context, &result_types, false)]) diff --git a/src/metadata/drop_overrides.rs b/src/metadata/drop_overrides.rs index 85d21ab1d..e0a1b30b8 100644 --- a/src/metadata/drop_overrides.rs +++ b/src/metadata/drop_overrides.rs @@ -32,7 +32,7 @@ use melior::{ ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, r#type::FunctionType, - Block, Location, Module, Region, Value, + Attribute, Block, Identifier, Location, Module, Region, Value, }, Context, }; @@ -85,7 +85,20 @@ impl DropOverridesMeta { StringAttribute::new(context, &format!("drop${}", id.id)), TypeAttribute::new(FunctionType::new(context, &[ty], &[]).into()), region, - &[], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "public").into(), + ), + ( + Identifier::new(context, "llvm.CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } diff --git a/src/metadata/dup_overrides.rs b/src/metadata/dup_overrides.rs index 164330963..dada18dd5 100644 --- a/src/metadata/dup_overrides.rs +++ b/src/metadata/dup_overrides.rs @@ -32,7 +32,7 @@ use melior::{ ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, r#type::FunctionType, - Block, Location, Module, Region, Value, ValueLike, + Attribute, Block, Identifier, Location, Module, Region, Value, ValueLike, }, Context, }; @@ -85,7 +85,20 @@ impl DupOverridesMeta { StringAttribute::new(context, &format!("dup${}", id.id)), TypeAttribute::new(FunctionType::new(context, &[ty], &[ty, ty]).into()), region, - &[], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "public").into(), + ), + ( + Identifier::new(context, "llvm.CConv"), + Attribute::parse(context, "#llvm.cconv").unwrap(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } diff --git a/src/metadata/runtime_bindings.rs b/src/metadata/runtime_bindings.rs index 78bf2eca0..3ddbf0441 100644 --- a/src/metadata/runtime_bindings.rs +++ b/src/metadata/runtime_bindings.rs @@ -9,7 +9,7 @@ use melior::{ ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, r#type::{FunctionType, IntegerType}, - Block, Identifier, Location, Module, OperationRef, Region, Value, + Attribute, Block, Identifier, Location, Module, OperationRef, Region, Value, }, Context, }; @@ -76,10 +76,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -128,10 +134,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -178,10 +190,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -220,10 +238,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -262,10 +286,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -299,10 +329,16 @@ impl RuntimeBindingsMeta { FunctionType::new(context, &[llvm::r#type::pointer(context, 0)], &[]).into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -345,10 +381,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -394,10 +436,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -442,10 +490,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], location, )); } @@ -489,10 +543,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -548,10 +608,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -604,10 +670,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -655,10 +727,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -708,10 +786,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -755,10 +839,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } @@ -809,10 +899,16 @@ impl RuntimeBindingsMeta { .into(), ), Region::new(), - &[( - Identifier::new(context, "sym_visibility"), - StringAttribute::new(context, "private").into(), - )], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "private").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], Location::unknown(context), )); } diff --git a/src/types/felt252_dict.rs b/src/types/felt252_dict.rs index 768f36d51..2de679d19 100644 --- a/src/types/felt252_dict.rs +++ b/src/types/felt252_dict.rs @@ -28,7 +28,7 @@ use melior::{ dialect::{func, llvm, ods}, ir::{ attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute}, - Block, Location, Module, Region, Type, + Attribute, Block, Identifier, Location, Module, Region, Type, }, Context, }; @@ -126,7 +126,16 @@ fn build_dup<'ctx>( false, )), region, - &[], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "public").into(), + ), + ( + Identifier::new(context, "linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], location, )); } @@ -191,7 +200,16 @@ fn build_drop<'ctx>( false, )), region, - &[], + &[ + ( + Identifier::new(context, "sym_visibility"), + StringAttribute::new(context, "public").into(), + ), + ( + Identifier::new(context, "llvm.linkage"), + Attribute::parse(context, "#llvm.linkage").unwrap(), + ), + ], location, )); diff --git a/src/utils.rs b/src/utils.rs index 146accc16..090b03292 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -642,8 +642,7 @@ pub mod test { .compile(program, false) .expect("Could not compile test program to MLIR."); - // FIXME: There are some bugs with non-zero LLVM optimization levels. - let executor = JitNativeExecutor::from_native_module(module, OptLevel::None); + let executor = JitNativeExecutor::from_native_module(module, OptLevel::Less); executor .invoke_dynamic_with_syscall_handler( entry_point_id, From b5769e4f6ba914b36eef68e0b1f71c791d7d075c Mon Sep 17 00:00:00 2001 From: Rodrigo Date: Wed, 16 Oct 2024 09:02:28 -0400 Subject: [PATCH 2/6] Resolve `CAIRO_NATIVE_RUNTIME_LIBRARY` relative path (#841) * feat(ffi): resolve runtime relative path using current dir * chore: remove mentions to old runtime variable * fix: typo --------- Co-authored-by: Bohdan Ohorodnii --- docs/execution_walkthrough.md | 2 +- scripts/bench-hyperfine.sh | 2 +- src/ffi.rs | 37 +++++++++++++++++++++++------------ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/docs/execution_walkthrough.md b/docs/execution_walkthrough.md index fd6b645bb..d5b78d26f 100644 --- a/docs/execution_walkthrough.md +++ b/docs/execution_walkthrough.md @@ -206,7 +206,7 @@ Builtin stats: BuiltinStats { bitwise: 1, ec_op: 0, range_check: 1, pedersen: 0, ## The Cairo Native runtime Sometimes we need to use stuff that would be too complicated or error-prone to implement in MLIR, but that we have readily available from Rust. That's when we use the runtime library. -When using the JIT it'll be automatically linked (if compiled with support for it, which is enabled by default). If using the AOT, the `CAIRO_NATIVE_RUNTIME_LIBDIR` environment variable will have to be modified to point to the directory that contains `libcairo_native_runtime.a`, which is built and placed in said folder by `make build`. +When using the JIT it'll be automatically linked (if compiled with support for it, which is enabled by default). If using the AOT, the `CAIRO_NATIVE_RUNTIME_LIBRARY` environment variable will have to be modified to point to the `libcairo_native_runtime.a` file, which is built and placed in said folder by `make build`. Although it's implemented in Rust, its functions use the C ABI and have Rust's name mangling disabled. This means that to the extern observer it's technically indistinguishible from a library written in C. By doing this we're making the functions callable from MLIR. diff --git a/scripts/bench-hyperfine.sh b/scripts/bench-hyperfine.sh index 47e9453c6..180120c79 100755 --- a/scripts/bench-hyperfine.sh +++ b/scripts/bench-hyperfine.sh @@ -90,7 +90,7 @@ run_bench() { -o "$OUTPUT_DIR/$base_name-march-native" \ >> /dev/stderr - CAIRO_NATIVE_RUNTIME_LIBDIR="$ROOT_DIR/target/release" hyperfine \ + hyperfine \ --warmup 3 \ --export-markdown "$OUTPUT_DIR/$base_name.md" \ --export-json "$OUTPUT_DIR/$base_name.json" \ diff --git a/src/ffi.rs b/src/ffi.rs index 1d1a7f412..09cccbbcf 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -29,6 +29,7 @@ use melior::ir::{Module, Type, TypeLike}; use mlir_sys::{mlirLLVMStructTypeGetElementType, mlirTranslateModuleToLLVMIR}; use std::{ borrow::Cow, + env, ffi::{CStr, CString}, io::Write, mem::MaybeUninit, @@ -222,6 +223,28 @@ pub fn object_to_shared_lib(object: &[u8], output_filename: &Path) -> Result<()> } } + let runtime_library_path = if let Ok(extra_dir) = std::env::var("CAIRO_NATIVE_RUNTIME_LIBRARY") + { + let path = Path::new(&extra_dir); + if path.is_absolute() { + extra_dir + } else { + let mut absolute_path = env::current_dir() + .expect("Failed to get the current directory") + .join(path); + absolute_path = absolute_path + .canonicalize() + .expect("Failed to cannonicalize path"); + String::from( + absolute_path + .to_str() + .expect("Absolute path contains non-utf8 characters"), + ) + } + } else { + String::from("libcairo_native_runtime.a") + }; + let args: Vec> = { #[cfg(target_os = "macos")] { @@ -239,14 +262,9 @@ pub fn object_to_shared_lib(object: &[u8], output_filename: &Path) -> Result<()> "-o".into(), Cow::from(output_path), "-lSystem".into(), + Cow::from(runtime_library_path), ]); - if let Ok(extra_dir) = std::env::var("CAIRO_NATIVE_RUNTIME_LIBRARY") { - args.extend([Cow::from(extra_dir)]); - } else { - args.extend(["libcairo_native_runtime.a".into()]); - } - args } #[cfg(target_os = "linux")] @@ -264,14 +282,9 @@ pub fn object_to_shared_lib(object: &[u8], output_filename: &Path) -> Result<()> Cow::from(output_path), "-lc".into(), Cow::from(file_path), + Cow::from(runtime_library_path), ]); - if let Ok(extra_dir) = std::env::var("CAIRO_NATIVE_RUNTIME_LIBRARY") { - args.extend([Cow::from(extra_dir)]); - } else { - args.extend(["libcairo_native_runtime.a".into()]); - } - args } #[cfg(target_os = "windows")] From 9db11752cbbe92f3abec69839b843d4c2302ff41 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 17 Oct 2024 16:11:29 +0200 Subject: [PATCH 3/6] try to fix ci (#858) --- .github/workflows/bench-hyperfine.yml | 6 +++--- .github/workflows/ci.yml | 14 +++++++------- .github/workflows/publish.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/rustdoc.yml | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/bench-hyperfine.yml b/.github/workflows/bench-hyperfine.yml index 79980ad24..6182f709d 100644 --- a/.github/workflows/bench-hyperfine.yml +++ b/.github/workflows/bench-hyperfine.yml @@ -16,7 +16,7 @@ env: jobs: bench-hyperfine: name: Hyperfine - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ @@ -97,7 +97,7 @@ jobs: matrix: branch: [ base, head ] name: Build cairo-native-run for ${{ matrix.branch }} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Cache binary uses: actions/cache@v3 @@ -171,7 +171,7 @@ jobs: hyperfine-prs: name: Bench PR (linux, amd64) needs: [ build-binaries ] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: PROGRAM: fib_2M OUTPUT_DIR: bench-outputs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14ae1da56..37d2f612e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: jobs: check: name: clippy - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ @@ -38,7 +38,7 @@ jobs: fmt: name: rustfmt - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.81.0 @@ -81,7 +81,7 @@ jobs: # Check for unnecessary dependencies. udeps: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ @@ -106,7 +106,7 @@ jobs: test: name: test (linux, amd64) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ @@ -185,7 +185,7 @@ jobs: coverage: name: coverage - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 strategy: matrix: partition: [1, 2, 3, 4] @@ -274,7 +274,7 @@ jobs: upload-coverage: name: Upload Coverage - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: [coverage] steps: - name: Setup rust env @@ -317,7 +317,7 @@ jobs: dockerfile: name: dockerfile (linux, amd64) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: check and free hdd space left diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8dc69fb33..994bdd853 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,7 +11,7 @@ on: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf8e81d31..d9ec91f76 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ on: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index c7a8b2fbb..afcc7c42f 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -11,7 +11,7 @@ permissions: jobs: publish-docs: name: GitHub Pages - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ From 0e251c56b8bb0c9c74c6de24575b21a5df2ba881 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 17 Oct 2024 17:23:43 +0200 Subject: [PATCH 4/6] update implementing libfuncs doc (#856) --- docs/implementing_libfuncs.md | 78 +++++++++++++++-------------------- src/arch.rs | 12 +++--- src/executor.rs | 4 +- 3 files changed, 41 insertions(+), 53 deletions(-) diff --git a/docs/implementing_libfuncs.md b/docs/implementing_libfuncs.md index 31b558713..aaef8d4d2 100644 --- a/docs/implementing_libfuncs.md +++ b/docs/implementing_libfuncs.md @@ -19,46 +19,45 @@ A type that doesn't have size would be `Layout::new::<()>()`, or if the type is a pointer like box: `Layout::new::<*mut ()>()` When adding a type, we also need to add the **serialization** and -**deserialization** functionality, so we can use it with the JIT runner. +**deserialization** functionality to convert the value to a memory representation that works with cairo-native. -You can find this functionality under `src/values.rs` and -`src/values/{typename}.rs`. As you can see, the project is quite organized -if you have a feel of its layout. +You can find this functionality under `src/values.rs`. -Serialization is done using `Serde`, and each type provides a `deserialize` -and `serialize` function. The inner workings of such functions can be a bit -complex due to how the JIT runner works. You need to work with pointers and -unsafe rust. +There is a `Value` enum with all the possible values that can be passed as input/output. -In `values.rs` we should also declare whether the type is complex under -`is_complex` in the `ValueBuilder` trait implementation. +This enum has a `impl` block with 2 important methods `to_ptr` and `from_ptr` which convert the Value into and from said +memory representation, held behind a pointer. + +When passing the values as inputs, there is one more required step, that is to pass the bytes of those values in the +target platform ABI compatible way, this is done with the `AbiArgument` trait and the `to_bytes` method. + +This trait, located in `src/arch.rs` is implemented currently for aarch64 and x86_64 (depending on the host platform) for some basic types, such as u64, u128, pointers, etc. Most importantly, it is also implemented for `ValueWithInfoWrapper` which allows to convert the Value using it's `to_ptr` method and correctly passing it as an argument in the given `buffer: &mut Vec`. + +In `types.rs` we should also declare whether the type is complex under +`is_complex`, whether its a builtin in `is_builtin`, a zst in `is_zst` and define it's layout in the `TypeBuilder` trait implementation. > Complex types are always passed by pointer (both as params and return > values) and require a stack allocation. Examples of complex values include > structs and enums, but not felts since LLVM considers them integers. ### Deserializing a type -When **deserializing** (a.k.a converting the inputs so the JIT runner +When **deserializing** (a.k.a converting the inputs so the runner accepts them), you are passed a bump allocator arena from `Bumpalo`, the general idea is to get the layout and size of the type, allocate it under -the arena, get a pointer, and return it. Which will later be passed to the -MLIR JIT runner. It is important the pointers passed are allocated by the +the arena, get a pointer, and return it (Some types require additional data on the heap, such as arrays, such allocations should be done with libc's malloc.). Which will later be passed to the runner. It is important the pointers passed are allocated by the arena and not Rust itself. -Then we need to hookup de `deserialize` method in `values.rs` `deserialize` -method. +This is done in the `to_ptr` method. ### Serializing a type When **serializing** a type, you will get a `ptr: NonNull<()>` (non null pointer), which you will have to cast, dereference and then deserialize. For a simple type to learn how it works, we recommend checking -`src/values/uint8.rs`, for more complex types, check `src/values/felt252.rs`. +`src/values.rs`, in the `from_ptr` method, look the u8 type in the match, for more complex types, check the felt252 type. The hardest types to understand are the enums, dictionaries and arrays, since they are complex types. -Then we need to hookup de `serialize` method in `values.rs` `serialize` method. - ### Implementing the library function Libfuncs are implemented under `src/libfuncs.rs` and `src/libfuncs/{libfunc_name}.rs`. Just like types. @@ -67,21 +66,15 @@ Using the `src/libfuncs/felt252.rs` libfuncs as a aid: ```rust,ignore /// Select and call the correct libfunc builder function from the selector. -pub fn build<'ctx, 'this, TType, TLibfunc>( +pub fn build<'ctx, 'this>( context: &'ctx Context, - registry: &ProgramRegistry, + registry: &ProgramRegistry, entry: &'this Block<'ctx>, location: Location<'ctx>, helper: &LibfuncHelper<'ctx, 'this>, metadata: &mut MetadataStorage, selector: &Felt252Concrete, -) -> Result<()> -where - TType: GenericType, - TLibfunc: GenericLibfunc, - ::Concrete: TypeBuilder, - ::Concrete: LibfuncBuilder, -{ +) -> Result<()> { match selector { Felt252Concrete::BinaryOperation(info) => { build_binary_operation(context, registry, entry, location, helper, metadata, info) @@ -109,11 +102,11 @@ An example libfunc, converting a u8 to a felt252, extensively commented: ```rust,ignore /// Generate MLIR operations for the `u8_to_felt252` libfunc. -pub fn build_to_felt252<'ctx, 'this, TType, TLibfunc>( +pub fn build_to_felt252<'ctx, 'this>( // The Context from MLIR, this is like the heart of the MLIR API, its required to create most stuff like types. context: &'ctx Context, // This is the sierra program registry, it aids us at finding types, functions, etc. - registry: &ProgramRegistry, + registry: &ProgramRegistry, // This is the MLIR entry block for this libfunc. Remember we append operations to blocks. entry: &'this Block<'ctx>, // The already created MLIR location for this libfunc, we need to pass this to all the MLIR operations. @@ -125,28 +118,24 @@ pub fn build_to_felt252<'ctx, 'this, TType, TLibfunc>( // The sierra information for this specific library function. This libfunc only contains signature information, but // others which are generic over a type will contain information about that type, for example array related libfuncs. info: &SignatureOnlyConcreteLibfunc, -) -> Result<()> -where - TType: GenericType, - TLibfunc: GenericLibfunc, - ::Concrete: TypeBuilder, - ::Concrete: LibfuncBuilder, -{ +) -> Result<()> { // We retrieve the felt252 type from the registry and call the "build" method to create the MLIR type. // We could also just call get_type() to hold on to the sierra type, and then `.layout(registry)` to get the type layout, // which is needed in some libfuncs doing more complex stuff. - let felt252_ty = registry - .get_type(&info.branch_signatures()[0].vars[0].ty)? - .build(context, helper, registry, metadata)?; + let felt252_ty = registry.build_type( + context, + helper, + registry, + metadata, + &info.branch_signatures()[0].vars[0].ty, + )?; // Retrieve the first argument passed to this library function, in this case its the u8 value we need to convert. let value: Value = entry.argument(0)?.into(); - // We create a "extui" operation from the "arith" dialect, which basically zero extends the value to have the same bits as the given type. - let op = entry.append_operation(arith::extui(value, felt252_ty, location)); - - // Get the result from the operation, in this case it's the extended value - let result = op.result(0)?.into(); + // We create a "extui" operation from the "arith" dialect, which basically + // zero extends the value to have the same bits as the given type. + let result = entry.append_op_result(arith::extui(value, felt252_ty, location))?; // Using the helper argument, append the branching operation to the next statement, passing result as our output variable. entry.append_operation(helper.br(0, &[result], location)); @@ -156,4 +145,3 @@ where ``` More info on the `extui` operation: - diff --git a/src/arch.rs b/src/arch.rs index 12d76b865..6017e3158 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -26,9 +26,9 @@ pub trait AbiArgument { fn to_bytes(&self, buffer: &mut Vec) -> Result<(), error::Error>; } -/// A wrapper that implements `AbiArgument` for `JitValue`s. It contains all the required stuff to -/// serialize all possible `JitValue`s. -pub struct JitValueWithInfoWrapper<'a> { +/// A wrapper that implements `AbiArgument` for `Value`s. It contains all the required stuff to +/// serialize all possible `Value`s. +pub struct ValueWithInfoWrapper<'a> { pub value: &'a Value, pub type_id: &'a ConcreteTypeId, pub info: &'a CoreTypeConcrete, @@ -37,12 +37,12 @@ pub struct JitValueWithInfoWrapper<'a> { pub registry: &'a ProgramRegistry, } -impl<'a> JitValueWithInfoWrapper<'a> { +impl<'a> ValueWithInfoWrapper<'a> { fn map<'b>( &'b self, value: &'b Value, type_id: &'b ConcreteTypeId, - ) -> Result, error::Error> + ) -> Result, error::Error> where 'b: 'a, { @@ -56,7 +56,7 @@ impl<'a> JitValueWithInfoWrapper<'a> { } } -impl<'a> AbiArgument for JitValueWithInfoWrapper<'a> { +impl<'a> AbiArgument for ValueWithInfoWrapper<'a> { fn to_bytes(&self, buffer: &mut Vec) -> Result<(), error::Error> { match (self.value, self.info) { (value, CoreTypeConcrete::Box(info)) => { diff --git a/src/executor.rs b/src/executor.rs index fc4b6823e..f88e87735 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -5,7 +5,7 @@ pub use self::{aot::AotNativeExecutor, contract::AotContractExecutor, jit::JitNativeExecutor}; use crate::{ - arch::{AbiArgument, JitValueWithInfoWrapper}, + arch::{AbiArgument, ValueWithInfoWrapper}, error::Error, execution_result::{BuiltinStats, ExecutionResult}, starknet::{handler::StarknetSyscallHandlerCallbacks, StarknetSyscallHandler}, @@ -184,7 +184,7 @@ fn invoke_dynamic( } } type_info if type_info.is_builtin() => 0u64.to_bytes(&mut invoke_data)?, - type_info => JitValueWithInfoWrapper { + type_info => ValueWithInfoWrapper { value: iter.next().unwrap(), type_id, info: type_info, From 530d1e95ecf9c9ad1ec2d1694fc1b82e082a3d45 Mon Sep 17 00:00:00 2001 From: Edgar Date: Fri, 18 Oct 2024 16:13:31 +0200 Subject: [PATCH 5/6] Add runtime tests (#857) * runtime tests * dict tests * improve dict test * refund * tests * fix runtime desc * remove old comment * manually drop target fd * avoid possible overflow * fix typo --- runtime/Cargo.toml | 7 +- runtime/src/lib.rs | 242 ++++++++++++++++++++++++++++++++++++++++- tests/common.rs | 2 +- tests/tests/boolean.rs | 2 - 4 files changed, 242 insertions(+), 11 deletions(-) diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index dc1ebd4c0..cdf46ad32 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -1,16 +1,19 @@ [package] name = "cairo-native-runtime" version = "0.2.0-alpha.4" -description = "A compiler to convert Cairo's intermediate representation Sierra code to MLIR." +description = "The runtime for cairo-native." edition = "2021" license = "Apache-2.0" +keywords = ["starknet", "cairo", "runtime"] [lib] crate-type = ["rlib", "cdylib", "staticlib"] [dependencies] starknet-types-core = { version = "0.1.7", default-features = false, features = [ - "std", "serde", "hash" + "std", + "serde", + "hash", ] } cairo-lang-sierra-gas = "2.8.4" starknet-curve = "0.5.1" diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index fabe8de68..5e2c75ae7 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -13,7 +13,9 @@ use starknet_types_core::{ felt::Felt, hash::StarkHash, }; -use std::{collections::HashMap, ffi::c_void, fs::File, io::Write, os::fd::FromRawFd}; +use std::{ + collections::HashMap, ffi::c_void, fs::File, io::Write, mem::ManuallyDrop, os::fd::FromRawFd, +}; use std::{ops::Mul, vec::IntoIter}; lazy_static! { @@ -39,7 +41,8 @@ pub unsafe extern "C" fn cairo_native__libfunc__debug__print( data: *const [u8; 32], len: u32, ) -> i32 { - let mut target = File::from_raw_fd(target_fd); + // Avoid closing `stdout` on all branches. + let mut target = ManuallyDrop::new(File::from_raw_fd(target_fd)); let mut items = Vec::with_capacity(len as usize); @@ -57,9 +60,6 @@ pub unsafe extern "C" fn cairo_native__libfunc__debug__print( return 1; }; - // Avoid closing `stdout`. - std::mem::forget(target); - 0 } @@ -248,7 +248,7 @@ pub unsafe extern "C" fn cairo_native__dict_get( #[no_mangle] pub unsafe extern "C" fn cairo_native__dict_gas_refund(ptr: *const FeltDict) -> u64 { let dict = &*ptr; - (dict.count - dict.inner.len() as u64) * *DICT_GAS_REFUND_PER_ACCESS + (dict.count.saturating_sub(dict.inner.len() as u64)) * *DICT_GAS_REFUND_PER_ACCESS } /// Compute `ec_point_from_x_nz(x)` and store it. @@ -648,3 +648,233 @@ pub fn as_cairo_short_string_ex(value: &Felt, length: usize) -> Option { Some(as_string) } + +#[cfg(test)] +mod tests { + use std::{ + env, + fs::{remove_file, File}, + io::{Read, Seek}, + os::{fd::AsRawFd, raw::c_void}, + }; + + use starknet_types_core::felt::Felt; + + use crate::{ + cairo_native__dict_drop, cairo_native__dict_dup, cairo_native__dict_gas_refund, + cairo_native__dict_get, cairo_native__dict_new, cairo_native__libfunc__debug__print, + cairo_native__libfunc__ec__ec_point_try_new_nz, cairo_native__libfunc__ec__ec_state_add, + cairo_native__libfunc__ec__ec_state_init, cairo_native__libfunc__hades_permutation, + cairo_native__libfunc__pedersen, + }; + + pub fn felt252_short_str(value: &str) -> Felt { + let values: Vec<_> = value + .chars() + .filter_map(|c| c.is_ascii().then_some(c as u8)) + .collect(); + + assert!(values.len() < 32); + Felt::from_bytes_be_slice(&values) + } + + #[test] + fn test_debug_print() { + let dir = env::temp_dir(); + remove_file(dir.join("print.txt")).ok(); + let mut file = File::create_new(dir.join("print.txt")).unwrap(); + { + let fd = file.as_raw_fd(); + let data = felt252_short_str("hello world"); + let data = data.to_bytes_le(); + unsafe { cairo_native__libfunc__debug__print(fd, &data, 1) }; + } + file.seek(std::io::SeekFrom::Start(0)).unwrap(); + + let mut result = String::new(); + file.read_to_string(&mut result).unwrap(); + + assert_eq!( + result, + "[DEBUG]\t0x68656c6c6f20776f726c64 ('hello world')\n" + ); + } + + #[test] + fn test_pederesen() { + let mut dst = [0; 32]; + let lhs = Felt::from(1).to_bytes_le(); + let rhs = Felt::from(3).to_bytes_le(); + + unsafe { + cairo_native__libfunc__pedersen(&mut dst, &lhs, &rhs); + } + + assert_eq!( + dst, + [ + 84, 98, 174, 134, 3, 124, 237, 179, 166, 110, 159, 98, 170, 35, 83, 237, 130, 154, + 236, 0, 205, 134, 200, 185, 39, 92, 0, 228, 132, 217, 130, 5 + ] + ) + } + + #[test] + fn test_hades_permutation() { + let mut op0 = Felt::from(1).to_bytes_le(); + let mut op1 = Felt::from(1).to_bytes_le(); + let mut op2 = Felt::from(1).to_bytes_le(); + + unsafe { + cairo_native__libfunc__hades_permutation(&mut op0, &mut op1, &mut op2); + } + + assert_eq!( + Felt::from_bytes_le(&op0), + Felt::from_hex("0x4ebdde1149fcacbb41e4fc342432a48c97994fd045f432ad234ae9279269779") + .unwrap() + ); + assert_eq!( + Felt::from_bytes_le(&op1), + Felt::from_hex("0x7f4cec57dd08b69414f7de7dffa230fc90fa3993673c422408af05831e0cc98") + .unwrap() + ); + assert_eq!( + Felt::from_bytes_le(&op2), + Felt::from_hex("0x5b5d00fd09caade43caffe70527fa84d5d9cd51e22c2ce115693ecbb5854d6a") + .unwrap() + ); + } + + // Test free_fn for the dict testing, values are u64. + pub extern "C" fn free_fn_test(ptr: *mut c_void) { + assert!(!ptr.is_null()); + let b: Box = unsafe { Box::from_raw(ptr.cast()) }; + drop(b); + } + + pub extern "C" fn dup_fn_test(ptr: *mut c_void) -> *mut c_void { + assert!(!ptr.is_null()); + let ptr: *mut u64 = ptr.cast(); + let dup = unsafe { Box::into_raw(Box::new(*ptr)) }; + dup.cast() + } + + #[test] + fn test_dict() { + let dict = unsafe { cairo_native__dict_new(free_fn_test) }; + + let key = Felt::ONE.to_bytes_le(); + + { + let ptr: *mut *mut u64 = unsafe { cairo_native__dict_get(&mut *dict, &key) }.cast(); + unsafe { *ptr = Box::into_raw(Box::new(2u64)) }; + } + + { + let ptr: *mut *mut u64 = unsafe { cairo_native__dict_get(&mut *dict, &key) }.cast(); + assert!(!ptr.is_null()); + assert!(!unsafe { *ptr }.is_null()); + assert_eq!(unsafe { **ptr }, 2); + } + + { + let refund = unsafe { cairo_native__dict_gas_refund(dict) }; + assert_eq!(refund, 4050); + } + + let cloned_dict = unsafe { cairo_native__dict_dup(dict, dup_fn_test) }; + + unsafe { cairo_native__dict_drop(dict, None) }; + + { + let ptr: *mut *mut u64 = + unsafe { cairo_native__dict_get(&mut *cloned_dict, &key) }.cast(); + assert!(!ptr.is_null()); + assert!(!unsafe { *ptr }.is_null()); + assert_eq!(unsafe { **ptr }, 2); + } + + unsafe { cairo_native__dict_drop(cloned_dict, None) }; + } + + #[test] + fn test_ec__ec_point() { + let mut state = [ + Felt::ZERO.to_bytes_le(), + Felt::ZERO.to_bytes_le(), + Felt::ZERO.to_bytes_le(), + Felt::ZERO.to_bytes_le(), + ]; + + unsafe { cairo_native__libfunc__ec__ec_state_init(&mut state) }; + + let points: &mut [[u8; 32]; 2] = (&mut state[..2]).try_into().unwrap(); + + let result = unsafe { cairo_native__libfunc__ec__ec_point_try_new_nz(points) }; + + // point should be valid since it was made with state init + assert!(result); + } + + #[test] + fn test_ec__ec_point_add() { + // Test values taken from starknet-rs + let mut state = [ + Felt::from_dec_str( + "874739451078007766457464989774322083649278607533249481151382481072868806602", + ) + .unwrap() + .to_bytes_le(), + Felt::from_dec_str( + "152666792071518830868575557812948353041420400780739481342941381225525861407", + ) + .unwrap() + .to_bytes_le(), + Felt::from_dec_str( + "874739451078007766457464989774322083649278607533249481151382481072868806602", + ) + .unwrap() + .to_bytes_le(), + Felt::from_dec_str( + "152666792071518830868575557812948353041420400780739481342941381225525861407", + ) + .unwrap() + .to_bytes_le(), + ]; + + let point = [ + Felt::from_dec_str( + "874739451078007766457464989774322083649278607533249481151382481072868806602", + ) + .unwrap() + .to_bytes_le(), + Felt::from_dec_str( + "152666792071518830868575557812948353041420400780739481342941381225525861407", + ) + .unwrap() + .to_bytes_le(), + ]; + + unsafe { + cairo_native__libfunc__ec__ec_state_add(&mut state, &point); + }; + + assert_eq!( + state[0], + Felt::from_dec_str( + "3324833730090626974525872402899302150520188025637965566623476530814354734325", + ) + .unwrap() + .to_bytes_le() + ); + assert_eq!( + state[1], + Felt::from_dec_str( + "3147007486456030910661996439995670279305852583596209647900952752170983517249", + ) + .unwrap() + .to_bytes_le() + ); + } +} diff --git a/tests/common.rs b/tests/common.rs index 9ca95d52b..b35a85e18 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -79,7 +79,7 @@ pub fn felt(value: &str) -> [u32; 8] { u32_digits.try_into().unwrap() } -/// Parse any time that can be a bigint to a felt that can be used in the cairo-native input. +/// Parse any type that can be a bigint to a felt that can be used in the cairo-native input. pub fn feltn(value: impl Into) -> [u32; 8] { let value: BigInt = value.into(); let value = match value.sign() { diff --git a/tests/tests/boolean.rs b/tests/tests/boolean.rs index 039018485..7e14f919f 100644 --- a/tests/tests/boolean.rs +++ b/tests/tests/boolean.rs @@ -106,8 +106,6 @@ lazy_static! { }; } -// Since comparing a felt to 1 to create boolean (uses felt252_is_zero and felt sub,add) has a bug, -// we'll be using use u8 on other tests until this is fixed. The bug may be in felt subtraction. #[test] fn felt252_to_bool_bug() { let program = &FELT252_TO_BOOL; From 4ba9e1609163c0d760f2a716244729d3116cc5ee Mon Sep 17 00:00:00 2001 From: Julian Gonzalez Calderon Date: Fri, 18 Oct 2024 11:27:35 -0300 Subject: [PATCH 6/6] Log contract compilation time (#823) * Add mlir compilation time * Add time to llvm compilation and object linking * Use finer grained timers * Change info to trace --- src/context.rs | 16 +++++++++++++++- src/ffi.rs | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/context.rs b/src/context.rs index c6b249219..f868c0faa 100644 --- a/src/context.rs +++ b/src/context.rs @@ -33,7 +33,8 @@ use mlir_sys::{ mlirLLVMDIModuleAttrGet, MlirLLVMDIEmissionKind_MlirLLVMDIEmissionKindFull, MlirLLVMDINameTableKind_MlirLLVMDINameTableKindDefault, }; -use std::sync::OnceLock; +use std::{sync::OnceLock, time::Instant}; +use tracing::trace; /// Context of IRs, dialects and passes for Cairo programs compilation. #[derive(Debug, Eq, PartialEq)] @@ -70,6 +71,9 @@ impl NativeContext { program: &Program, ignore_debug_names: bool, ) -> Result { + trace!("starting sierra to mlir compilation"); + let pre_sierra_compilation_instant = Instant::now(); + static INITIALIZED: OnceLock<()> = OnceLock::new(); INITIALIZED.get_or_init(|| unsafe { LLVM_InitializeAllTargets(); @@ -181,6 +185,12 @@ impl NativeContext { ignore_debug_names, )?; + let sierra_compilation_time = pre_sierra_compilation_instant.elapsed().as_millis(); + trace!( + time = sierra_compilation_time, + "sierra to mlir compilation finished" + ); + if let Ok(x) = std::env::var("NATIVE_DEBUG_DUMP") { if x == "1" || x == "true" { std::fs::write("dump-prepass.mlir", module.as_operation().to_string()) @@ -202,7 +212,11 @@ impl NativeContext { } } + trace!("starting mlir passes"); + let pre_passes_instant = Instant::now(); run_pass_manager(&self.context, &mut module)?; + let passes_time = pre_passes_instant.elapsed().as_millis(); + trace!(time = passes_time, "mlir passes finished"); if let Ok(x) = std::env::var("NATIVE_DEBUG_DUMP") { if x == "1" || x == "true" { diff --git a/src/ffi.rs b/src/ffi.rs index 09cccbbcf..0dbb8aa1d 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -36,8 +36,10 @@ use std::{ path::Path, ptr::{addr_of_mut, null_mut}, sync::OnceLock, + time::Instant, }; use tempfile::NamedTempFile; +use tracing::trace; /// For any `!llvm.struct<...>` type, return the MLIR type of the field at the requested index. pub fn get_struct_field_type_at<'c>(r#type: &Type<'c>, index: usize) -> Type<'c> { @@ -110,7 +112,11 @@ pub fn module_to_object(module: &Module<'_>, opt_level: OptLevel) -> Result, opt_level: OptLevel) -> Result 3, }; let passes = CString::new(format!("default")).unwrap(); + + trace!("starting llvm passes"); + let pre_passes_instant = Instant::now(); let error = LLVMRunPasses(llvm_module, passes.as_ptr(), machine, opts); + let passes_time = pre_passes_instant.elapsed().as_millis(); + trace!(time = passes_time, "llvm passes finished"); + if !error.is_null() { let msg = LLVMGetErrorMessage(error); let msg = CStr::from_ptr(msg); @@ -171,6 +183,8 @@ pub fn module_to_object(module: &Module<'_>, opt_level: OptLevel) -> Result = MaybeUninit::uninit(); + trace!("starting llvm to object compilation"); + let pre_llvm_compilation_instant = Instant::now(); let ok = LLVMTargetMachineEmitToMemoryBuffer( machine, llvm_module, @@ -178,6 +192,11 @@ pub fn module_to_object(module: &Module<'_>, opt_level: OptLevel) -> Result Result<()> }; let mut linker = std::process::Command::new("ld"); + + trace!("starting linking"); + let pre_linking_instant = Instant::now(); let proc = linker.args(args.iter().map(|x| x.as_ref())).output()?; + let linking_time = pre_linking_instant.elapsed().as_millis(); + trace!(time = linking_time, "linking finished"); + if proc.status.success() { Ok(()) } else {