diff --git a/corelib.patch b/corelib.patch index 8071ddd9d..aa6d50972 100644 --- a/corelib.patch +++ b/corelib.patch @@ -1028,23 +1028,6 @@ diff --color=auto -crN cairo2/corelib/src/test.cairo cairo2/corelib_1/src/test.c *** cairo2/corelib/src/test.cairo Mon Aug 25 09:50:36 2025 --- cairo2/corelib_1/src/test.cairo Wed Sep 3 12:26:58 2025 *************** -*** 12,18 **** - mod ec_test; - mod felt_test; - mod fmt_test; -! mod gas_reserve_test; - mod hash_test; - mod integer_test; - mod iter_test; ---- 12,18 ---- - mod ec_test; - mod felt_test; - mod fmt_test; -! // mod gas_reserve_test; - mod hash_test; - mod integer_test; - mod iter_test; -*************** *** 24,30 **** mod option_test; mod plugins_test; diff --git a/debug_utils/sierra-emu/src/value.rs b/debug_utils/sierra-emu/src/value.rs index ad8d3a257..1a1e0a6da 100644 --- a/debug_utils/sierra-emu/src/value.rs +++ b/debug_utils/sierra-emu/src/value.rs @@ -238,9 +238,9 @@ impl Value { StarknetTypeConcrete::Sha256StateHandle(_) => matches!(self, Self::Struct { .. }), }, CoreTypeConcrete::IntRange(_) => matches!(self, Self::IntRange { .. }), + CoreTypeConcrete::GasReserve(_) => matches!(self, Self::U128(_)), CoreTypeConcrete::Blake(_) => todo!(), CoreTypeConcrete::QM31(_) => todo!(), - CoreTypeConcrete::GasReserve(_) => todo!(), }; if !res { diff --git a/debug_utils/sierra-emu/src/vm.rs b/debug_utils/sierra-emu/src/vm.rs index 26fd7e300..a87d0159b 100644 --- a/debug_utils/sierra-emu/src/vm.rs +++ b/debug_utils/sierra-emu/src/vm.rs @@ -48,6 +48,7 @@ mod felt252_dict; mod felt252_dict_entry; mod function_call; mod gas; +mod gas_reserve; mod int; mod int_range; mod jump; @@ -520,12 +521,12 @@ fn eval<'a>( EvalAction::NormalBranch(0, smallvec![value]) } CoreConcreteLibfunc::IntRange(selector) => self::int_range::eval(registry, selector, args), + CoreConcreteLibfunc::GasReserve(selector) => self::gas_reserve::eval(selector, args), CoreConcreteLibfunc::Blake(_) => todo!(), CoreConcreteLibfunc::QM31(_) => todo!(), CoreConcreteLibfunc::Felt252SquashedDict(_) => todo!(), CoreConcreteLibfunc::Trace(_) => todo!(), CoreConcreteLibfunc::UnsafePanic(_) => todo!(), CoreConcreteLibfunc::DummyFunctionCall(_) => todo!(), - CoreConcreteLibfunc::GasReserve(_) => todo!(), } } diff --git a/debug_utils/sierra-emu/src/vm/gas_reserve.rs b/debug_utils/sierra-emu/src/vm/gas_reserve.rs new file mode 100644 index 000000000..368caf769 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/gas_reserve.rs @@ -0,0 +1,39 @@ +use cairo_lang_sierra::extensions::gas_reserve::GasReserveConcreteLibfunc; +use smallvec::smallvec; + +use crate::{vm::EvalAction, Value}; + +pub fn eval(selector: &GasReserveConcreteLibfunc, args: Vec) -> EvalAction { + match selector { + GasReserveConcreteLibfunc::Create(_) => eval_gas_reserve_create(args), + GasReserveConcreteLibfunc::Utilize(_) => eval_gas_reserve_utilize(args), + } +} + +fn eval_gas_reserve_create(args: Vec) -> EvalAction { + let [range_check @ Value::Unit, Value::U64(gas), Value::U128(amount)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + if amount <= gas.into() { + let spare_gas = gas - amount as u64; + EvalAction::NormalBranch( + 0, + smallvec![range_check, Value::U64(spare_gas), Value::U128(amount)], + ) + } else { + EvalAction::NormalBranch(1, smallvec![range_check, Value::U64(gas)]) + } +} + +fn eval_gas_reserve_utilize(args: Vec) -> EvalAction { + let [Value::U64(gas), Value::U128(amount)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let updated_gas = gas + amount as u64; + + EvalAction::NormalBranch(0, smallvec![Value::U64(updated_gas)]) +} diff --git a/src/libfuncs.rs b/src/libfuncs.rs index 970e1eb93..72a0c80ca 100644 --- a/src/libfuncs.rs +++ b/src/libfuncs.rs @@ -63,6 +63,7 @@ mod felt252_dict; mod felt252_dict_entry; mod function_call; mod gas; +mod gas_reserve; mod int; mod int_range; mod mem; @@ -255,9 +256,11 @@ impl LibfuncBuilder for CoreConcreteLibfunc { metadata, &info.signature.param_signatures, ), + Self::GasReserve(selector) => self::gas_reserve::build( + context, registry, entry, location, helper, metadata, selector, + ), Self::QM31(_) => native_panic!("Implement QM31 libfunc"), Self::UnsafePanic(_) => native_panic!("Implement unsafe_panic libfunc"), - Self::GasReserve(_) => native_panic!("Implement gas_reserve libfunc"), Self::DummyFunctionCall(_) => native_panic!("Implement dummy_function_call libfunc"), } } diff --git a/src/libfuncs/gas_reserve.rs b/src/libfuncs/gas_reserve.rs new file mode 100644 index 000000000..2c0707a8a --- /dev/null +++ b/src/libfuncs/gas_reserve.rs @@ -0,0 +1,198 @@ +use crate::libfuncs::LibfuncHelper; +use crate::{error::Result, metadata::MetadataStorage}; +use cairo_lang_sierra::extensions::lib_func::SignatureOnlyConcreteLibfunc; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + gas_reserve::GasReserveConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use melior::dialect::arith::{self, CmpiPredicate}; +use melior::helpers::{ArithBlockExt, BuiltinBlockExt}; +use melior::ir::r#type::IntegerType; +use melior::{ + ir::{Block, Location}, + Context, +}; + +pub fn build<'ctx, 'this>( + context: &'ctx Context, + registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + metadata: &mut MetadataStorage, + selector: &GasReserveConcreteLibfunc, +) -> Result<()> { + match selector { + GasReserveConcreteLibfunc::Create(info) => { + build_gas_reserve_create(context, registry, entry, location, helper, metadata, info) + } + GasReserveConcreteLibfunc::Utilize(info) => { + build_gas_reserve_utilize(context, registry, entry, location, helper, metadata, info) + } + } +} + +/// Generate MLIR operations for the `gas_reserve_create` libfunc. +/// +/// # Cairo Signature +/// +/// ```cairo +/// pub extern fn gas_reserve_create( +/// amount: u128, +/// ) -> Option implicits(RangeCheck, GasBuiltin) nopanic; +/// ``` +fn build_gas_reserve_create<'ctx, 'this>( + context: &'ctx Context, + _registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + _metadata: &mut MetadataStorage, + _info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> { + let range_check = super::increment_builtin_counter(context, entry, location, entry.arg(0)?)?; + let current_gas = entry.arg(1)?; // u64 + let amount = entry.arg(2)?; // u128 + + let amount_ty = IntegerType::new(context, 128).into(); + let current_gas_128 = entry.append_op_result(arith::extui(current_gas, amount_ty, location))?; + let enough_gas = entry.cmpi( + context, + CmpiPredicate::Uge, + current_gas_128, + amount, + location, + )?; + + let gas_builtin_ty = IntegerType::new(context, 64).into(); + let spare_gas_128 = entry.append_op_result(arith::subi(current_gas_128, amount, location))?; + let spare_gas = + entry.append_op_result(arith::trunci(spare_gas_128, gas_builtin_ty, location))?; + + helper.cond_br( + context, + entry, + enough_gas, + [0, 1], + [ + &[range_check, spare_gas, amount], + &[range_check, current_gas], + ], + location, + ) +} + +/// Generate MLIR operations for the `gas_reserve_utilize` libfunc. +/// +/// # Cairo Signature +/// +/// ```cairo +/// pub extern fn gas_reserve_utilize(reserve: GasReserve) implicits(GasBuiltin) nopanic; +/// ``` +fn build_gas_reserve_utilize<'ctx, 'this>( + context: &'ctx Context, + _registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + _metadata: &mut MetadataStorage, + _info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> { + let current_gas = entry.arg(0)?; // u64 + let gas_reserve_128 = entry.arg(1)?; // u128 + + let gas_reserve = entry.append_op_result(arith::trunci( + gas_reserve_128, + IntegerType::new(context, 64).into(), + location, + ))?; + let updated_gas = entry.append_op_result(arith::addi(current_gas, gas_reserve, location))?; + + helper.br(entry, 0, &[updated_gas], location) +} + +#[cfg(test)] +mod test { + use crate::{load_cairo, utils::testing::run_program, Value}; + + #[test] + fn run_create() { + let program = load_cairo!( + use core::gas::{GasReserve, gas_reserve_create, gas_reserve_utilize}; + + fn run_test_1() -> Option { + gas_reserve_create(100) + } + + fn run_test_2(amount: u128) -> u128 { + let initial_gas = core::testing::get_available_gas(); + let reserve = gas_reserve_create(amount).unwrap(); + let final_gas = core::testing::get_available_gas(); + gas_reserve_utilize(reserve); + + initial_gas - final_gas + } + ); + + let result = run_program(&program, "run_test_1", &[]).return_value; + if let Value::Enum { tag, value, .. } = result { + assert_eq!(tag, 0); + assert_eq!(value, Box::new(Value::Uint128(100))) + } + + let amount = 100; + let result = run_program(&program, "run_test_2", &[Value::Uint128(amount)]).return_value; + if let Value::Enum { tag, value, .. } = result { + if let Value::Struct { fields, .. } = *value { + assert_eq!(tag, 0); + assert_eq!(fields[0], Value::Uint128(amount)); + } + } + + let amount = 700; + let result = run_program(&program, "run_test_2", &[Value::Uint128(amount)]).return_value; + if let Value::Enum { tag, value, .. } = result { + if let Value::Struct { fields, .. } = *value { + assert_eq!(tag, 0); + assert_eq!(fields[0], Value::Uint128(amount)); + } + } + } + + #[test] + fn run_utilize() { + let program = load_cairo!( + use core::gas::{GasReserve, gas_reserve_create, gas_reserve_utilize}; + + fn run_test(amount: u128) -> u128 { + let initial_gas = core::testing::get_available_gas(); + let reserve = gas_reserve_create(amount).unwrap(); + gas_reserve_utilize(reserve); + let final_gas = core::testing::get_available_gas(); + + initial_gas - final_gas + } + ); + + let gas_quant = 10; + let result = run_program(&program, "run_test", &[Value::Uint128(gas_quant)]).return_value; + if let Value::Enum { tag, value, .. } = result { + if let Value::Struct { fields, .. } = *value { + assert_eq!(tag, 0); + assert_eq!(fields[0], Value::Uint128(0)); + } + } + + let gas_quant = 1000; + let result = run_program(&program, "run_test", &[Value::Uint128(gas_quant)]).return_value; + if let Value::Enum { tag, value, .. } = result { + if let Value::Struct { fields, .. } = *value { + assert_eq!(tag, 0); + assert_eq!(fields[0], Value::Uint128(0)); + } + } + } +} diff --git a/src/types.rs b/src/types.rs index 72fedf180..7f7a47598 100644 --- a/src/types.rs +++ b/src/types.rs @@ -46,6 +46,7 @@ mod felt252; mod felt252_dict; mod felt252_dict_entry; mod gas_builtin; +mod gas_reserve; mod int_range; mod non_zero; mod nullable; @@ -465,9 +466,15 @@ impl TypeBuilder for CoreTypeConcrete { metadata, WithSelf::new(self_ty, info), ), + CoreTypeConcrete::GasReserve(info) => self::gas_reserve::build( + context, + module, + registry, + metadata, + WithSelf::new(self_ty, info), + ), Self::Blake(_) => native_panic!("Build Blake type"), CoreTypeConcrete::QM31(_) => native_panic!("Build QM31 type"), - CoreTypeConcrete::GasReserve(_) => native_panic!("Build GasReserve type"), } } @@ -574,9 +581,9 @@ impl TypeBuilder for CoreTypeConcrete { CoreTypeConcrete::Circuit(info) => circuit::is_complex(info), CoreTypeConcrete::IntRange(_info) => false, + CoreTypeConcrete::GasReserve(_info) => false, CoreTypeConcrete::Blake(_info) => native_panic!("Implement is_complex for Blake type"), CoreTypeConcrete::QM31(_info) => native_panic!("Implement is_complex for QM31 type"), - CoreTypeConcrete::GasReserve(_info) => native_panic!("Implement is_complex for GasReserve type"), }) } @@ -650,6 +657,7 @@ impl TypeBuilder for CoreTypeConcrete { } CoreTypeConcrete::BoundedInt(_) => false, + CoreTypeConcrete::GasReserve(_info) => false, CoreTypeConcrete::Const(info) => { let type_info = registry.get_type(&info.inner_ty)?; type_info.is_zst(registry)? @@ -663,9 +671,6 @@ impl TypeBuilder for CoreTypeConcrete { } CoreTypeConcrete::Blake(_info) => native_panic!("Implement is_zst for Blake type"), CoreTypeConcrete::QM31(_info) => native_panic!("Implement is_zst for QM31 type"), - CoreTypeConcrete::GasReserve(_info) => { - native_panic!("Implement is_zst for GasReserve type") - } }) } @@ -764,6 +769,7 @@ impl TypeBuilder for CoreTypeConcrete { CoreTypeConcrete::Sint128(_) => get_integer_layout(128), CoreTypeConcrete::Bytes31(_) => get_integer_layout(248), CoreTypeConcrete::BoundedInt(info) => get_integer_layout(info.range.offset_bit_width()), + CoreTypeConcrete::GasReserve(_info) => get_integer_layout(128), CoreTypeConcrete::Const(const_type) => { registry.get_type(&const_type.inner_ty)?.layout(registry)? @@ -778,9 +784,6 @@ impl TypeBuilder for CoreTypeConcrete { } CoreTypeConcrete::Blake(_info) => native_panic!("Implement layout for Blake type"), CoreTypeConcrete::QM31(_info) => native_panic!("Implement layout for QM31 type"), - CoreTypeConcrete::GasReserve(_info) => { - native_panic!("Implement layout for GasReserve type") - } } .pad_to_align()) } @@ -871,10 +874,8 @@ impl TypeBuilder for CoreTypeConcrete { .is_memory_allocated(registry)?, CoreTypeConcrete::Coupon(_) => false, CoreTypeConcrete::Circuit(_) => false, + CoreTypeConcrete::GasReserve(_) => false, CoreTypeConcrete::QM31(_) => native_panic!("Implement is_memory_allocated for QM31"), - CoreTypeConcrete::GasReserve(_) => { - native_panic!("Implement is_memory_allocated for GasReserve") - } }) } diff --git a/src/types/gas_reserve.rs b/src/types/gas_reserve.rs new file mode 100644 index 000000000..74055e04b --- /dev/null +++ b/src/types/gas_reserve.rs @@ -0,0 +1,26 @@ +use super::WithSelf; +use crate::{error::Result, metadata::MetadataStorage}; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + types::InfoOnlyConcreteType, + }, + program_registry::ProgramRegistry, +}; +use melior::{ + ir::{r#type::IntegerType, Module, Type}, + Context, +}; + +/// Build the MLIR type. +/// +/// Check out [the module](self) for more info. +pub fn build<'ctx>( + context: &'ctx Context, + _module: &Module<'ctx>, + _registry: &ProgramRegistry, + _metadata: &mut MetadataStorage, + _info: WithSelf, +) -> Result> { + Ok(IntegerType::new(context, 128).into()) +} diff --git a/src/values.rs b/src/values.rs index c561cf6ba..66b3e2c82 100644 --- a/src/values.rs +++ b/src/values.rs @@ -1016,11 +1016,9 @@ impl Value { y: y.into(), } } + CoreTypeConcrete::GasReserve(_) => Self::Uint128(*ptr.cast::().as_ref()), CoreTypeConcrete::Blake(_) => native_panic!("Implement from_ptr for Blake type"), CoreTypeConcrete::QM31(_) => native_panic!("Implement from_ptr for QM31 type"), - CoreTypeConcrete::GasReserve(_) => { - native_panic!("Implement from_ptr for GasReserve type") - } } }) }