From 5b4a41922a622a969e640a8a7edc2f55b055a4c4 Mon Sep 17 00:00:00 2001 From: Timofey Date: Sun, 3 Dec 2023 11:59:37 +0000 Subject: [PATCH] feat: Add 'free_range' syscall (#3467) Co-authored-by: Gregory Sobol --- core-backend/src/env.rs | 1 + core-backend/src/funcs.rs | 32 +++- core-backend/src/memory.rs | 10 +- core-backend/src/mock.rs | 3 + core-processor/src/ext.rs | 35 +++- core/src/costs.rs | 14 +- core/src/env.rs | 8 +- core/src/memory.rs | 51 +++++- gcli/src/meta/executor.rs | 1 + gsdk/src/metadata/generated.rs | 2 + pallets/gear/src/benchmarking/mod.rs | 22 +++ pallets/gear/src/benchmarking/syscalls.rs | 37 +++++ .../benchmarking/tests/syscalls_integrity.rs | 154 +++++++----------- pallets/gear/src/schedule.rs | 10 ++ pallets/gear/src/tests.rs | 154 ++++++++++++++++++ pallets/gear/src/weights.rs | 42 +++++ runtime/vara/src/weights/pallet_gear.rs | 46 +++++- utils/regression-analysis/src/main.rs | 2 + utils/wasm-gen/src/config/syscalls/param.rs | 1 + .../src/generator/syscalls/invocator.rs | 34 +++- utils/wasm-instrument/src/syscalls.rs | 15 +- 21 files changed, 542 insertions(+), 132 deletions(-) diff --git a/core-backend/src/env.rs b/core-backend/src/env.rs index f9d973d2b95..858cd8b395a 100644 --- a/core-backend/src/env.rs +++ b/core-backend/src/env.rs @@ -241,6 +241,7 @@ where builder.add_func(Alloc, wrap_syscall!(alloc)); builder.add_func(Free, wrap_syscall!(free)); + builder.add_func(FreeRange, wrap_syscall!(free_range)); } } diff --git a/core-backend/src/funcs.rs b/core-backend/src/funcs.rs index 265a1aae110..e83f219811f 100644 --- a/core-backend/src/funcs.rs +++ b/core-backend/src/funcs.rs @@ -70,12 +70,6 @@ impl From for SyscallValue { } } -impl From for SyscallValue { - fn from(value: i64) -> Self { - SyscallValue(Value::I64(value)) - } -} - impl TryFrom for u32 { type Error = HostError; @@ -675,6 +669,32 @@ where }) } + pub fn free_range(start: u32, end: u32) -> impl Syscall { + InfallibleSyscall::new(RuntimeCosts::FreeRange, move |ctx: &mut CallerWrap| { + let page_err = |_| { + UndefinedTerminationReason::Actor(ActorTerminationReason::Trap( + TrapExplanation::Unknown, + )) + }; + + let start = WasmPage::new(start).map_err(page_err)?; + let end = WasmPage::new(end).map_err(page_err)?; + + let result = ctx.ext_mut().free_range(start, end); + + match ctx.process_alloc_func_result(result)? { + Ok(()) => { + log::trace!("Free range {start:?}:{end:?} success"); + Ok(0) + } + Err(e) => { + log::trace!("Free range {start:?}:{end:?} failed: {e}"); + Ok(1) + } + } + }) + } + pub fn env_vars(vars_ver: u32, vars_ptr: u32) -> impl Syscall { InfallibleSyscall::new(RuntimeCosts::EnvVars, move |ctx: &mut CallerWrap| { let vars = ctx.ext_mut().env_vars(vars_ver)?; diff --git a/core-backend/src/memory.rs b/core-backend/src/memory.rs index 626757962a5..5db2ce99655 100644 --- a/core-backend/src/memory.rs +++ b/core-backend/src/memory.rs @@ -534,7 +534,7 @@ mod tests { Ok(137.into()) ); - // if we have 2 in a row we can allocate even 2 + // if we free 2 in a row we can allocate even 2 ctx.free(117.into()).unwrap(); ctx.free(118.into()).unwrap(); @@ -543,6 +543,14 @@ mod tests { Ok(117.into()) ); + // same as above, if we free_range 2 in a row we can allocate 2 + ctx.free_range(117.into()..=118.into()).unwrap(); + + assert_eq!( + ctx.alloc::(2.into(), &mut mem_wrap, |_| Ok(())), + Ok(117.into()) + ); + // but if 2 are not in a row, bad luck ctx.free(117.into()).unwrap(); ctx.free(158.into()).unwrap(); diff --git a/core-backend/src/mock.rs b/core-backend/src/mock.rs index 84f55e70774..7f577544136 100644 --- a/core-backend/src/mock.rs +++ b/core-backend/src/mock.rs @@ -115,6 +115,9 @@ impl Externalities for MockExt { fn free(&mut self, _page: WasmPage) -> Result<(), Self::AllocError> { Err(Error) } + fn free_range(&mut self, _start: WasmPage, _end: WasmPage) -> Result<(), Self::AllocError> { + Err(Error) + } fn env_vars(&self, version: u32) -> Result { match version { 1 => Ok(EnvVars::V1(EnvVarsV1 { diff --git a/core-processor/src/ext.rs b/core-processor/src/ext.rs index 9c56457f97b..54e6b20f5bf 100644 --- a/core-processor/src/ext.rs +++ b/core-processor/src/ext.rs @@ -40,7 +40,7 @@ use gear_core::{ ContextOutcomeDrain, ContextStore, Dispatch, GasLimit, HandlePacket, InitPacket, MessageContext, Packet, ReplyPacket, }, - pages::{GearPage, PageU32Size, WasmPage}, + pages::{GearPage, PageNumber, PageU32Size, WasmPage}, program::MemoryInfix, reservation::GasReserver, }; @@ -799,6 +799,30 @@ impl Externalities for Ext { .map_err(Into::into) } + fn free_range(&mut self, start: WasmPage, end: WasmPage) -> Result<(), Self::AllocError> { + let page_count: u32 = end + .checked_sub(start) + .ok_or(AllocExtError::Alloc(AllocError::InvalidFreeRange( + start.into(), + end.into(), + )))? + .into(); + + Ext::charge_gas_if_enough( + &mut self.context.gas_counter, + &mut self.context.gas_allowance_counter, + self.context + .host_fn_weights + .free_range_per_page + .saturating_mul(page_count as u64), + )?; + + self.context + .allocations_context + .free_range(start..=end) + .map_err(Into::into) + } + fn env_vars(&self, version: u32) -> Result { match version { 1 => Ok(EnvVars::V1(EnvVarsV1 { @@ -1243,10 +1267,7 @@ impl Externalities for Ext { mod tests { use super::*; use alloc::vec; - use gear_core::{ - message::{ContextSettings, IncomingDispatch, Payload, MAX_PAYLOAD_SIZE}, - pages::PageNumber, - }; + use gear_core::message::{ContextSettings, IncomingDispatch, Payload, MAX_PAYLOAD_SIZE}; struct MessageContextBuilder { incoming_dispatch: IncomingDispatch, @@ -1363,12 +1384,12 @@ mod tests { ); // Freeing existing page. - // Counters still shouldn't be changed. + // Counters shouldn't be changed. assert!(ext.free(existing_page).is_ok()); assert_eq!(ext.gas_left(), gas_left); // Freeing non existing page. - // Counters shouldn't be changed. + // Counters still shouldn't be changed. assert_eq!( ext.free(non_existing_page), Err(AllocExtError::Alloc(AllocError::InvalidFree( diff --git a/core/src/costs.rs b/core/src/costs.rs index 52ba67d015c..9cbe724bfb4 100644 --- a/core/src/costs.rs +++ b/core/src/costs.rs @@ -94,9 +94,15 @@ pub struct HostFnWeights { /// Weight per allocated page for `alloc`. pub alloc_per_page: u64, - /// Weight of calling `alloc`. + /// Weight of calling `free`. pub free: u64, + /// Weight of calling `free_range` + pub free_range: u64, + + /// Weight of calling `free_range` per page + pub free_range_per_page: u64, + /// Weight of calling `gr_reserve_gas`. pub gr_reserve_gas: u64, @@ -326,6 +332,10 @@ pub enum RuntimeCosts { Alloc(u32), /// Weight of calling `free`. Free, + /// Base weight of calling `free_range` + FreeRange, + /// Weight of calling `free_range` per amount of pages. + FreeRangePerPage(u32), /// Weight of calling `gr_reserve_gas`. ReserveGas, /// Weight of calling `gr_unreserve_gas`. @@ -467,6 +477,8 @@ impl RuntimeCosts { Null => 0, Alloc(pages) => cost_with_weight_per_page(s.alloc, s.alloc_per_page, pages), Free => s.free, + FreeRange => s.free_range, + FreeRangePerPage(pages) => cost_with_weight_per_page(0, s.free_range_per_page, pages), ReserveGas => s.gr_reserve_gas, UnreserveGas => s.gr_unreserve_gas, SystemReserveGas => s.gr_system_reserve_gas, diff --git a/core/src/env.rs b/core/src/env.rs index 7f89688cd7e..dde033306e3 100644 --- a/core/src/env.rs +++ b/core/src/env.rs @@ -192,12 +192,12 @@ pub trait Externalities { mem: &mut impl Memory, ) -> Result; - /// Free specific memory page. - /// - /// Unlike traditional allocator, if multiple pages allocated via `alloc`, all pages - /// should be `free`-d separately. + /// Free specific page. fn free(&mut self, page: WasmPage) -> Result<(), Self::AllocError>; + /// Free specific memory range. + fn free_range(&mut self, start: WasmPage, end: WasmPage) -> Result<(), Self::AllocError>; + /// Get environment variables currently set in the system and in the form /// corresponded to the requested version. fn env_vars(&self, version: u32) -> Result; diff --git a/core/src/memory.rs b/core/src/memory.rs index 10bb65bd74f..a86643128f9 100644 --- a/core/src/memory.rs +++ b/core/src/memory.rs @@ -29,7 +29,7 @@ use core::{ fmt, fmt::Debug, iter, - ops::{Deref, DerefMut}, + ops::{Deref, DerefMut, RangeInclusive}, }; use scale_info::{ scale::{self, Decode, Encode, EncodeLike, Input, Output}, @@ -270,6 +270,9 @@ pub enum AllocError { /// outside additionally allocated for this program. #[display(fmt = "Page {_0} cannot be freed by the current program")] InvalidFree(u32), + /// Invalid range for free_range + #[display(fmt = "Invalid range {_0}:{_1} for free_range")] + InvalidFreeRange(u32, u32), /// Gas charge error #[from] #[display(fmt = "{_0}")] @@ -376,15 +379,29 @@ impl AllocationsContext { Ok(start) } - /// Free specific page. - /// - /// Currently running program should own this page. + /// Free specific memory page. pub fn free(&mut self, page: WasmPage) -> Result<(), AllocError> { - if page < self.static_pages || page >= self.max_pages || !self.allocations.remove(&page) { - Err(AllocError::InvalidFree(page.0)) - } else { - Ok(()) + if page < self.static_pages || page >= self.max_pages { + return Err(AllocError::InvalidFree(page.0)); + } + + if !self.allocations.remove(&page) { + return Err(AllocError::InvalidFree(page.0)); + } + + Ok(()) + } + + /// Try to free pages in range. Will only return error if range is invalid. + /// + /// Currently running program should own this pages. + pub fn free_range(&mut self, range: RangeInclusive) -> Result<(), AllocError> { + if *range.start() < self.static_pages || *range.end() >= self.max_pages { + return Err(AllocError::InvalidFreeRange(range.start().0, range.end().0)); } + + self.allocations.retain(|p| !range.contains(p)); + Ok(()) } /// Decomposes this instance and returns allocations. @@ -458,6 +475,13 @@ mod tests { let mut ctx = AllocationsContext::new(BTreeSet::from([WasmPage(0)]), WasmPage(1), WasmPage(1)); assert_eq!(ctx.free(WasmPage(1)), Err(AllocError::InvalidFree(1))); + + let mut ctx = AllocationsContext::new( + BTreeSet::from([WasmPage(1), WasmPage(3)]), + WasmPage(1), + WasmPage(4), + ); + assert_eq!(ctx.free_range(WasmPage(1)..=WasmPage(3)), Ok(())); } #[test] @@ -540,13 +564,15 @@ mod tests { enum Action { Alloc { pages: WasmPage }, Free { page: WasmPage }, + FreeRange { page: WasmPage, size: u8 }, } fn actions() -> impl Strategy> { let action = wasm_page_number().prop_flat_map(|page| { prop_oneof![ Just(Action::Alloc { pages: page }), - Just(Action::Free { page }) + Just(Action::Free { page }), + any::().prop_map(move |size| Action::FreeRange { page, size }), ] }); proptest::collection::vec(action, 0..1024) @@ -579,6 +605,7 @@ mod tests { fn assert_free_error(err: AllocError) { match err { AllocError::InvalidFree(_) => {} + AllocError::InvalidFreeRange(_, _) => {} err => panic!("{err:?}"), } } @@ -610,6 +637,12 @@ mod tests { assert_free_error(err); } } + Action::FreeRange { page, size } => { + let end = WasmPage::from(page.0.saturating_add(size as u32) as u16); + if let Err(err) = ctx.free_range(page..=end) { + assert_free_error(err); + } + } } } } diff --git a/gcli/src/meta/executor.rs b/gcli/src/meta/executor.rs index 42381ae2328..b15069d8dfa 100644 --- a/gcli/src/meta/executor.rs +++ b/gcli/src/meta/executor.rs @@ -131,6 +131,7 @@ mod env { "gr_size" => gr_size(store, memory), // methods may be used by programs but not required by metadata. "free" => func!(@result store, i32), + "free_range" => func!(@result store, i32, i32), "gr_block_height" => func!(store, u32), "gr_block_timestamp" => func!(store, u32), "gr_create_program_wgas" => func!(store, i32, i32, u32, i32, u32, u64, u32, i32), diff --git a/gsdk/src/metadata/generated.rs b/gsdk/src/metadata/generated.rs index 2024928555f..b64cac4330e 100644 --- a/gsdk/src/metadata/generated.rs +++ b/gsdk/src/metadata/generated.rs @@ -2778,6 +2778,8 @@ pub mod runtime_types { pub alloc: runtime_types::sp_weights::weight_v2::Weight, pub alloc_per_page: runtime_types::sp_weights::weight_v2::Weight, pub free: runtime_types::sp_weights::weight_v2::Weight, + pub free_range: runtime_types::sp_weights::weight_v2::Weight, + pub free_range_per_page: runtime_types::sp_weights::weight_v2::Weight, pub gr_reserve_gas: runtime_types::sp_weights::weight_v2::Weight, pub gr_unreserve_gas: runtime_types::sp_weights::weight_v2::Weight, pub gr_system_reserve_gas: runtime_types::sp_weights::weight_v2::Weight, diff --git a/pallets/gear/src/benchmarking/mod.rs b/pallets/gear/src/benchmarking/mod.rs index f829491f962..b911e5bcc97 100644 --- a/pallets/gear/src/benchmarking/mod.rs +++ b/pallets/gear/src/benchmarking/mod.rs @@ -826,6 +826,28 @@ benchmarks! { verify_process(res.unwrap()); } + free_range { + let r in 0 .. API_BENCHMARK_BATCHES; + let mut res = None; + let exec = Benches::::free_range(r, 1)?; + }: { + res.replace(run_process(exec)); + } + verify { + verify_process(res.unwrap()); + } + + free_range_per_page { + let p in 1 .. API_BENCHMARK_BATCHES; + let mut res = None; + let exec = Benches::::free_range(1, p)?; + }: { + res.replace(run_process(exec)); + } + verify { + verify_process(res.unwrap()); + } + gr_reserve_gas { let r in 0 .. T::ReservationsLimit::get() as u32; let mut res = None; diff --git a/pallets/gear/src/benchmarking/syscalls.rs b/pallets/gear/src/benchmarking/syscalls.rs index 4e0b6484fa9..411b1a55476 100644 --- a/pallets/gear/src/benchmarking/syscalls.rs +++ b/pallets/gear/src/benchmarking/syscalls.rs @@ -262,6 +262,43 @@ where Self::prepare_handle(module, 0) } + pub fn free_range(repetitions: u32, pages_per_call: u32) -> Result, &'static str> { + use Instruction::*; + + let n_pages = repetitions.checked_mul(pages_per_call).unwrap(); + assert!(n_pages <= max_pages::() as u32); + + let mut instructions = vec![]; + for _ in 0..API_BENCHMARK_BATCH_SIZE { + instructions.extend([I32Const(n_pages as i32), Call(0), I32Const(-1)]); + unreachable_condition(&mut instructions, I32Eq); // if alloc returns -1 then it's error + + for i in 0..repetitions { + let start = i.checked_mul(pages_per_call).unwrap(); + let end = pages_per_call + .checked_sub(1) + .and_then(|x| start.checked_add(x)) + .unwrap(); + instructions.extend([ + I32Const(start as i32), + I32Const(end as i32), + Call(1), + I32Const(0), + ]); + unreachable_condition(&mut instructions, I32Ne); + } + } + + let module = ModuleDefinition { + memory: Some(ImportedMemory::new(0)), + imported_functions: vec![SyscallName::Alloc, SyscallName::FreeRange], + handle_body: Some(body::from_instructions(instructions)), + ..Default::default() + }; + + Self::prepare_handle(module, 0) + } + pub fn gr_reserve_gas(r: u32) -> Result, &'static str> { let repetitions = r; let res_offset = COMMON_OFFSET; diff --git a/pallets/gear/src/benchmarking/tests/syscalls_integrity.rs b/pallets/gear/src/benchmarking/tests/syscalls_integrity.rs index 3a6938a92bc..f5a8bca4b8e 100644 --- a/pallets/gear/src/benchmarking/tests/syscalls_integrity.rs +++ b/pallets/gear/src/benchmarking/tests/syscalls_integrity.rs @@ -178,8 +178,11 @@ where | SyscallName::Debug | SyscallName::Panic | SyscallName::OomPanic => {/* tests here aren't required, read module docs for more info */}, - SyscallName::Alloc => check_mem::(false), - SyscallName::Free => check_mem::(true), + + SyscallName::Alloc + | SyscallName::Free + | SyscallName::FreeRange => check_mem::(), + SyscallName::OutOfGas => { /*no need for tests */} SyscallName::Random => check_gr_random::(), SyscallName::ReserveGas => check_gr_reserve_gas::(), @@ -380,7 +383,7 @@ where }); } -fn check_mem(check_free: bool) +fn check_mem() where T: Config, T::AccountId: Origin, @@ -412,23 +415,25 @@ where utils::run_to_next_block::(None); // no errors occurred + assert!(Gear::::is_initialized(pid)); + assert!(Gear::::is_active(pid)); assert!(MailboxOf::::is_empty(&default_account)); - if check_free { - Gear::::send_message( - RawOrigin::Signed(default_account.clone()).into(), - pid, - b"".to_vec(), - 50_000_000_000, - 0u128.unique_saturated_into(), - false, - ) - .expect("failed to send message to test program"); - utils::run_to_next_block::(None); + Gear::::send_message( + RawOrigin::Signed(default_account.clone()).into(), + pid, + b"".to_vec(), + 50_000_000_000, + 0u128.unique_saturated_into(), + false, + ) + .expect("failed to send message to test program"); + utils::run_to_next_block::(None); - // no errors occurred - assert!(MailboxOf::::is_empty(&default_account)); - } + // no errors occurred + assert!(Gear::::is_initialized(pid)); + assert!(Gear::::is_active(pid)); + assert!(MailboxOf::::is_empty(&default_account)); Gear::::reset(); } @@ -1209,70 +1214,6 @@ where .into() } -// (module -// (import "env" "memory" (memory 1)) -// (import "env" "alloc" (func $alloc (param i32) (result i32))) -// (import "env" "free" (func $free (param i32))) -// (export "init" (func $init)) -// (export "handle" (func $handle)) -// (func $init -// ;; allocate 2 more pages with expected starting index 1 -// (block -// i32.const 0x2 -// call $alloc -// i32.const 0x1 -// i32.eq -// br_if 0 -// unreachable -// ) -// ;; put to page with index 2 (the third) some value -// (block -// i32.const 0x20001 -// i32.const 0x63 -// i32.store -// ) -// ;; put to page with index 1 (the second) some value -// (block -// i32.const 0x10001 -// i32.const 0x64 -// i32.store -// ) -// ;; check it has the value -// (block -// i32.const 0x10001 -// i32.load -// i32.const 0x65 -// i32.eq -// br_if 0 -// unreachable -// ) -// ;; remove page with index 1 (the second page) -// (block -// i32.const 0x1 -// call $free -// ) -// ) -// (func $handle -// ;; check that the second page is empty -// (block -// i32.const 0x10001 -// i32.load -// i32.const 0x0 -// i32.eq -// br_if 0 -// unreachable -// ) -// ;; check that the third page has data -// (block -// i32.const 0x20001 -// i32.load -// i32.const 0x63 -// i32.eq -// br_if 0 -// unreachable -// ) -// ) -// ) fn alloc_free_test_wasm() -> WasmModule where T::AccountId: Origin, @@ -1281,53 +1222,67 @@ where ModuleDefinition { memory: Some(ImportedMemory::new(1)), - imported_functions: vec![SyscallName::Alloc, SyscallName::Free], + imported_functions: vec![ + SyscallName::Alloc, + SyscallName::Free, + SyscallName::FreeRange, + ], init_body: Some(FuncBody::new( vec![], Instructions::new(vec![ - // ;; allocate 2 more pages with expected starting index 1 + // allocate 5 pages Instruction::Block(BlockType::NoResult), - Instruction::I32Const(0x2), + Instruction::I32Const(0x5), Instruction::Call(0), Instruction::I32Const(0x1), Instruction::I32Eq, Instruction::BrIf(0), Instruction::Unreachable, Instruction::End, - // ;; put to page with index 2 (the third) some value + // put some values in pages 2-5 Instruction::Block(BlockType::NoResult), + Instruction::I32Const(0x10001), + Instruction::I32Const(0x61), + Instruction::I32Store(2, 0), Instruction::I32Const(0x20001), + Instruction::I32Const(0x62), + Instruction::I32Store(2, 0), + Instruction::I32Const(0x30001), Instruction::I32Const(0x63), Instruction::I32Store(2, 0), - Instruction::End, - // ;; put to page with index 1 (the second) some value - Instruction::Block(BlockType::NoResult), - Instruction::I32Const(0x10001), + Instruction::I32Const(0x40001), Instruction::I32Const(0x64), Instruction::I32Store(2, 0), Instruction::End, - // ;; check it has the value + // check it has the value Instruction::Block(BlockType::NoResult), Instruction::I32Const(0x10001), Instruction::I32Load(2, 0), - Instruction::I32Const(0x64), + Instruction::I32Const(0x61), Instruction::I32Eq, Instruction::BrIf(0), Instruction::Unreachable, Instruction::End, - // ;; remove page with index 1 (the second page) + // free second page Instruction::Block(BlockType::NoResult), Instruction::I32Const(0x1), Instruction::Call(1), Instruction::Drop, Instruction::End, + // free_range pages 2-4 + Instruction::Block(BlockType::NoResult), + Instruction::I32Const(0x1), + Instruction::I32Const(0x3), + Instruction::Call(2), + Instruction::Drop, + Instruction::End, Instruction::End, ]), )), handle_body: Some(FuncBody::new( vec![], Instructions::new(vec![ - // ;; check that the second page is empty + // check that the second page is empty Instruction::Block(BlockType::NoResult), Instruction::I32Const(0x10001), Instruction::I32Load(2, 0), @@ -1336,11 +1291,20 @@ where Instruction::BrIf(0), Instruction::Unreachable, Instruction::End, - // ;; check that the third page has data + // check that the 3rd page is empty Instruction::Block(BlockType::NoResult), Instruction::I32Const(0x20001), Instruction::I32Load(2, 0), - Instruction::I32Const(0x63), + Instruction::I32Const(0x0), + Instruction::I32Eq, + Instruction::BrIf(0), + Instruction::Unreachable, + Instruction::End, + // check that the 5th page still has data + Instruction::Block(BlockType::NoResult), + Instruction::I32Const(0x40001), + Instruction::I32Load(2, 0), + Instruction::I32Const(0x64), Instruction::I32Eq, Instruction::BrIf(0), Instruction::Unreachable, diff --git a/pallets/gear/src/schedule.rs b/pallets/gear/src/schedule.rs index 5e86704f868..737251e29d2 100644 --- a/pallets/gear/src/schedule.rs +++ b/pallets/gear/src/schedule.rs @@ -344,6 +344,12 @@ pub struct HostFnWeights { /// Weight of calling `free`. pub free: Weight, + /// Weight of calling `free_range`. + pub free_range: Weight, + + /// Weight of calling `free_range` per page. + pub free_range_per_page: Weight, + /// Weight of calling `gr_reserve_gas`. pub gr_reserve_gas: Weight, @@ -857,6 +863,8 @@ impl HostFnWeights { alloc: self.alloc.ref_time(), alloc_per_page: self.alloc_per_page.ref_time(), free: self.free.ref_time(), + free_range: self.free_range.ref_time(), + free_range_per_page: self.free_range_per_page.ref_time(), gr_reserve_gas: self.gr_reserve_gas.ref_time(), gr_unreserve_gas: self.gr_unreserve_gas.ref_time(), gr_system_reserve_gas: self.gr_system_reserve_gas.ref_time(), @@ -979,6 +987,8 @@ impl Default for HostFnWeights { .saturating_sub(to_weight!(cost_batched!(mem_grow))), alloc_per_page: to_weight!(cost_batched!(alloc_per_page)), free: to_weight!(cost_batched!(free)), + free_range: to_weight!(cost_batched!(free_range)), + free_range_per_page: to_weight!(cost_batched!(free_range_per_page)), gr_reserve_gas: to_weight!(cost!(gr_reserve_gas)), gr_system_reserve_gas: to_weight!(cost_batched!(gr_system_reserve_gas)), gr_unreserve_gas: to_weight!(cost!(gr_unreserve_gas)), diff --git a/pallets/gear/src/tests.rs b/pallets/gear/src/tests.rs index 2bbb59d5f83..5b778587a69 100644 --- a/pallets/gear/src/tests.rs +++ b/pallets/gear/src/tests.rs @@ -13904,6 +13904,160 @@ fn free_usage_error() { }); } +#[test] +fn free_range_oob_error() { + const WAT: &str = r#" +(module + (import "env" "memory" (memory 1)) + (import "env" "free_range" (func $free_range (param i32) (param i32) (result i32))) + (export "init" (func $init)) + (func $init + ;; free impossible and non-existing range + i32.const 0x0 + i32.const 0xffffff + call $free_range + i32.const 0x0 + i32.ne + if + unreachable + end + ) +) + "#; + + init_logger(); + new_test_ext().execute_with(|| { + let pid = Gear::upload_program( + RuntimeOrigin::signed(USER_1), + ProgramCodeKind::Custom(WAT).to_bytes(), + DEFAULT_SALT.to_vec(), + EMPTY_PAYLOAD.to_vec(), + 10_000_000_000_u64, + 0, + false, + ) + .map(|_| get_last_program_id()) + .unwrap(); + let mid = get_last_message_id(); + + run_to_next_block(None); + + assert!(Gear::is_terminated(pid)); + assert_failed( + mid, + ActorExecutionErrorReplyReason::Trap(TrapExplanation::Unknown), + ); + }); +} + +#[test] +fn free_range_invalid_range_error() { + const WAT: &str = r#" +(module + (import "env" "memory" (memory 1)) + (import "env" "free_range" (func $free_range (param i32) (param i32) (result i32))) + (export "init" (func $init)) + (func $init + ;; free invalid range (start > end) + i32.const 0x55 + i32.const 0x2 + call $free_range + i32.const 0x1 ;; we expect an error + i32.ne + if + unreachable + end + ) +) + "#; + + init_logger(); + new_test_ext().execute_with(|| { + let pid = Gear::upload_program( + RuntimeOrigin::signed(USER_1), + ProgramCodeKind::Custom(WAT).to_bytes(), + DEFAULT_SALT.to_vec(), + EMPTY_PAYLOAD.to_vec(), + 500_000_000_u64, + 0, + false, + ) + .map(|_| get_last_program_id()) + .unwrap(); + let mid = get_last_message_id(); + + run_to_next_block(None); + assert!(!Gear::is_terminated(pid)); + assert_succeed(mid); + }); +} + +#[test] +fn free_range_success() { + const WAT: &str = r#" +(module + (import "env" "memory" (memory 1)) + (import "env" "alloc" (func $alloc (param i32) (result i32))) + (import "env" "free" (func $free (param i32) (result i32))) + (import "env" "free_range" (func $free_range (param i32) (param i32) (result i32))) + (export "init" (func $init)) + (func $init + ;; allocate 4 pages + i32.const 0x4 + call $alloc + + i32.const 1 + i32.ne + if + unreachable + end + + ;; free one page in range + i32.const 0x2 + call $free + + i32.const 0 + i32.ne + if + unreachable + end + + ;; free range with one missing page + i32.const 0x1 + i32.const 0x4 + call $free_range + i32.const 0x0 + i32.ne + if + unreachable + end + ) +) + "#; + + init_logger(); + new_test_ext().execute_with(|| { + let pid = Gear::upload_program( + RuntimeOrigin::signed(USER_1), + ProgramCodeKind::Custom(WAT).to_bytes(), + DEFAULT_SALT.to_vec(), + EMPTY_PAYLOAD.to_vec(), + 500_000_000_u64, + 0, + false, + ) + .map(|_| get_last_program_id()) + .unwrap(); + let mid = get_last_message_id(); + + run_to_next_block(None); + + assert_succeed(mid); + assert!(Gear::is_initialized(pid)); + assert!(Gear::is_active(pid)); + }); +} + #[test] fn reject_incorrect_stack_pointer() { let wat = format!( diff --git a/pallets/gear/src/weights.rs b/pallets/gear/src/weights.rs index a71ad0a9798..b0695a26165 100644 --- a/pallets/gear/src/weights.rs +++ b/pallets/gear/src/weights.rs @@ -69,6 +69,8 @@ pub trait WeightInfo { fn alloc(r: u32, ) -> Weight; fn alloc_per_page(p: u32, ) -> Weight; fn free(r: u32, ) -> Weight; + fn free_range(r: u32, ) -> Weight; + fn free_range_per_page(p: u32, ) -> Weight; fn gr_reserve_gas(r: u32, ) -> Weight; fn gr_unreserve_gas(r: u32, ) -> Weight; fn gr_system_reserve_gas(r: u32, ) -> Weight; @@ -594,6 +596,26 @@ impl WeightInfo for SubstrateWeight { // Standard Error: 269_511 .saturating_add(Weight::from_parts(63_993_415, 0).saturating_mul(r.into())) } + /// The range of component `r` is `[0, 20]`. + fn free_range(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 229_654_000 picoseconds. + Weight::from_parts(251_181_937, 0) + // Standard Error: 228_273 + .saturating_add(Weight::from_parts(76_272_378, 0).saturating_mul(r.into())) + } + /// The range of component `p` is `[1, 20]`. + fn free_range_per_page(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 305_902_000 picoseconds. + Weight::from_parts(302_932_691, 0) + // Standard Error: 60_684 + .saturating_add(Weight::from_parts(5_306_653, 0).saturating_mul(p.into())) + } /// The range of component `r` is `[0, 256]`. fn gr_reserve_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: @@ -2565,6 +2587,26 @@ impl WeightInfo for () { // Standard Error: 269_511 .saturating_add(Weight::from_parts(63_993_415, 0).saturating_mul(r.into())) } + /// The range of component `r` is `[0, 20]`. + fn free_range(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 229_654_000 picoseconds. + Weight::from_parts(251_181_937, 0) + // Standard Error: 228_273 + .saturating_add(Weight::from_parts(76_272_378, 0).saturating_mul(r.into())) + } + /// The range of component `p` is `[1, 20]`. + fn free_range_per_page(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 305_902_000 picoseconds. + Weight::from_parts(302_932_691, 0) + // Standard Error: 60_684 + .saturating_add(Weight::from_parts(5_306_653, 0).saturating_mul(p.into())) + } /// The range of component `r` is `[0, 256]`. fn gr_reserve_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: diff --git a/runtime/vara/src/weights/pallet_gear.rs b/runtime/vara/src/weights/pallet_gear.rs index b0219137a90..76d4cbfb0af 100644 --- a/runtime/vara/src/weights/pallet_gear.rs +++ b/runtime/vara/src/weights/pallet_gear.rs @@ -19,13 +19,13 @@ //! Autogenerated weights for pallet_gear //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-11-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("vara-dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/gear benchmark pallet --chain=vara-dev --steps=50 --repeat=20 --pallet=pallet_gear --extrinsic=alloc,alloc_in_handle,alloc_per_page,claim_value,create_program,db_read_per_kb,db_write_per_kb,free,gr_block_height,gr_block_timestamp,gr_create_program,gr_create_program_per_kb,gr_create_program_wgas,gr_create_program_wgas_per_kb,gr_debug,gr_debug_per_kb,gr_gas_available,gr_message_id,gr_pay_program_rent,gr_program_id,gr_random,gr_read,gr_read_per_kb,gr_reply_code,gr_reply_deposit,gr_reply_per_kb,gr_reply_push,gr_reply_push_input,gr_reply_push_input_per_kb,gr_reply_push_per_kb,gr_reply_to,gr_reply_wgas_per_kb,gr_reservation_reply_commit_per_kb,gr_reservation_reply_per_kb,gr_reservation_send,gr_reservation_send_commit,gr_reservation_send_per_kb,gr_reserve_gas,gr_send,gr_send_commit,gr_send_commit_wgas,gr_send_init,gr_send_input,gr_send_input_wgas,gr_send_per_kb,gr_send_push,gr_send_push_input,gr_send_push_input_per_kb,gr_send_push_per_kb,gr_send_wgas,gr_send_wgas_per_kb,gr_signal_code,gr_signal_from,gr_size,gr_source,gr_system_reserve_gas,gr_unreserve_gas,gr_value,gr_value_available,gr_wake,initial_allocation,instantiate_module_per_kb,instr_br,instr_br_if,instr_br_table,instr_br_table_per_entry,instr_call,instr_call_const,instr_call_indirect,instr_call_indirect_per_param,instr_call_per_local,instr_global_get,instr_global_set,instr_i32add,instr_i32and,instr_i32clz,instr_i32ctz,instr_i32divs,instr_i32divu,instr_i32eq,instr_i32eqz,instr_i32extend16s,instr_i32extend8s,instr_i32ges,instr_i32geu,instr_i32gts,instr_i32gtu,instr_i32les,instr_i32leu,instr_i32load,instr_i32lts,instr_i32ltu,instr_i32mul,instr_i32ne,instr_i32or,instr_i32popcnt,instr_i32rems,instr_i32remu,instr_i32rotl,instr_i32rotr,instr_i32shl,instr_i32shrs,instr_i32shru,instr_i32store,instr_i32sub,instr_i32wrapi64,instr_i32xor,instr_i64add,instr_i64and,instr_i64clz,instr_i64ctz,instr_i64divs,instr_i64divu,instr_i64eq,instr_i64eqz,instr_i64extend16s,instr_i64extend32s,instr_i64extend8s,instr_i64extendsi32,instr_i64extendui32,instr_i64ges,instr_i64geu,instr_i64gts,instr_i64gtu,instr_i64les,instr_i64leu,instr_i64load,instr_i64lts,instr_i64ltu,instr_i64mul,instr_i64ne,instr_i64or,instr_i64popcnt,instr_i64rems,instr_i64remu,instr_i64rotl,instr_i64rotr,instr_i64shl,instr_i64shrs,instr_i64shru,instr_i64store,instr_i64sub,instr_i64xor,instr_if,instr_local_get,instr_local_set,instr_local_tee,instr_memory_current,instr_select,lazy_pages_host_func_read,lazy_pages_host_func_write,lazy_pages_host_func_write_after_read,lazy_pages_load_page_storage_data,lazy_pages_signal_read,lazy_pages_signal_write,lazy_pages_signal_write_after_read,mem_grow,pay_program_rent,reinstrument_per_kb,resume_session_commit,resume_session_init,resume_session_push,send_message,send_reply,tasks_pause_program,tasks_pause_program_uninited,tasks_remove_from_mailbox,tasks_remove_from_waitlist,tasks_remove_gas_reservation,tasks_remove_resume_session,tasks_send_dispatch,tasks_send_user_message,tasks_send_user_message_to_mailbox,tasks_wake_message,tasks_wake_message_no_wake,upload_code,upload_program,gr_env_vars --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./scripts/benchmarking/weights-output/pallet_gear.rs --template=.maintain/frame-weight-template.hbs +// ./target/production/gear benchmark pallet --chain=vara-dev --steps=50 --repeat=20 --pallet=pallet_gear --extrinsic=alloc,alloc_in_handle,alloc_per_page,claim_value,create_program,db_read_per_kb,db_write_per_kb,free,free_range,free_range_per_page,gr_block_height,gr_block_timestamp,gr_create_program,gr_create_program_per_kb,gr_create_program_wgas,gr_create_program_wgas_per_kb,gr_debug,gr_debug_per_kb,gr_env_vars,gr_gas_available,gr_message_id,gr_pay_program_rent,gr_program_id,gr_random,gr_read,gr_read_per_kb,gr_reply_code,gr_reply_deposit,gr_reply_per_kb,gr_reply_push,gr_reply_push_input,gr_reply_push_input_per_kb,gr_reply_push_per_kb,gr_reply_to,gr_reply_wgas_per_kb,gr_reservation_reply_commit_per_kb,gr_reservation_reply_per_kb,gr_reservation_send,gr_reservation_send_commit,gr_reservation_send_per_kb,gr_reserve_gas,gr_send,gr_send_commit,gr_send_commit_wgas,gr_send_init,gr_send_input,gr_send_input_wgas,gr_send_per_kb,gr_send_push,gr_send_push_input,gr_send_push_input_per_kb,gr_send_push_per_kb,gr_send_wgas,gr_send_wgas_per_kb,gr_signal_code,gr_signal_from,gr_size,gr_source,gr_system_reserve_gas,gr_unreserve_gas,gr_value,gr_value_available,gr_wake,initial_allocation,instantiate_module_per_kb,instr_br,instr_br_if,instr_br_table,instr_br_table_per_entry,instr_call,instr_call_const,instr_call_indirect,instr_call_indirect_per_param,instr_call_per_local,instr_global_get,instr_global_set,instr_i32add,instr_i32and,instr_i32clz,instr_i32ctz,instr_i32divs,instr_i32divu,instr_i32eq,instr_i32eqz,instr_i32extend16s,instr_i32extend8s,instr_i32ges,instr_i32geu,instr_i32gts,instr_i32gtu,instr_i32les,instr_i32leu,instr_i32load,instr_i32lts,instr_i32ltu,instr_i32mul,instr_i32ne,instr_i32or,instr_i32popcnt,instr_i32rems,instr_i32remu,instr_i32rotl,instr_i32rotr,instr_i32shl,instr_i32shrs,instr_i32shru,instr_i32store,instr_i32sub,instr_i32wrapi64,instr_i32xor,instr_i64add,instr_i64and,instr_i64clz,instr_i64ctz,instr_i64divs,instr_i64divu,instr_i64eq,instr_i64eqz,instr_i64extend16s,instr_i64extend32s,instr_i64extend8s,instr_i64extendsi32,instr_i64extendui32,instr_i64ges,instr_i64geu,instr_i64gts,instr_i64gtu,instr_i64les,instr_i64leu,instr_i64load,instr_i64lts,instr_i64ltu,instr_i64mul,instr_i64ne,instr_i64or,instr_i64popcnt,instr_i64rems,instr_i64remu,instr_i64rotl,instr_i64rotr,instr_i64shl,instr_i64shrs,instr_i64shru,instr_i64store,instr_i64sub,instr_i64xor,instr_if,instr_local_get,instr_local_set,instr_local_tee,instr_memory_current,instr_select,lazy_pages_host_func_read,lazy_pages_host_func_write,lazy_pages_host_func_write_after_read,lazy_pages_load_page_storage_data,lazy_pages_signal_read,lazy_pages_signal_write,lazy_pages_signal_write_after_read,mem_grow,pay_program_rent,reinstrument_per_kb,resume_session_commit,resume_session_init,resume_session_push,send_message,send_reply,tasks_pause_program,tasks_pause_program_uninited,tasks_remove_from_mailbox,tasks_remove_from_waitlist,tasks_remove_gas_reservation,tasks_remove_resume_session,tasks_send_dispatch,tasks_send_user_message,tasks_send_user_message_to_mailbox,tasks_wake_message,tasks_wake_message_no_wake,upload_code,upload_program --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./scripts/benchmarking/weights-output/pallet_gear.rs --template=.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -69,6 +69,8 @@ pub trait WeightInfo { fn alloc(r: u32, ) -> Weight; fn alloc_per_page(p: u32, ) -> Weight; fn free(r: u32, ) -> Weight; + fn free_range(r: u32, ) -> Weight; + fn free_range_per_page(p: u32, ) -> Weight; fn gr_reserve_gas(r: u32, ) -> Weight; fn gr_unreserve_gas(r: u32, ) -> Weight; fn gr_system_reserve_gas(r: u32, ) -> Weight; @@ -594,6 +596,26 @@ impl pallet_gear::WeightInfo for SubstrateWeight { // Standard Error: 269_511 .saturating_add(Weight::from_parts(63_993_415, 0).saturating_mul(r.into())) } + /// The range of component `r` is `[0, 20]`. + fn free_range(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 229_654_000 picoseconds. + Weight::from_parts(251_181_937, 0) + // Standard Error: 228_273 + .saturating_add(Weight::from_parts(76_272_378, 0).saturating_mul(r.into())) + } + /// The range of component `p` is `[1, 20]`. + fn free_range_per_page(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 305_902_000 picoseconds. + Weight::from_parts(302_932_691, 0) + // Standard Error: 60_684 + .saturating_add(Weight::from_parts(5_306_653, 0).saturating_mul(p.into())) + } /// The range of component `r` is `[0, 256]`. fn gr_reserve_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: @@ -2565,6 +2587,26 @@ impl WeightInfo for () { // Standard Error: 269_511 .saturating_add(Weight::from_parts(63_993_415, 0).saturating_mul(r.into())) } + /// The range of component `r` is `[0, 20]`. + fn free_range(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 229_654_000 picoseconds. + Weight::from_parts(251_181_937, 0) + // Standard Error: 228_273 + .saturating_add(Weight::from_parts(76_272_378, 0).saturating_mul(r.into())) + } + /// The range of component `p` is `[1, 20]`. + fn free_range_per_page(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 305_902_000 picoseconds. + Weight::from_parts(302_932_691, 0) + // Standard Error: 60_684 + .saturating_add(Weight::from_parts(5_306_653, 0).saturating_mul(p.into())) + } /// The range of component `r` is `[0, 256]`. fn gr_reserve_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: diff --git a/utils/regression-analysis/src/main.rs b/utils/regression-analysis/src/main.rs index 94a7903bacf..1d72cac6ece 100644 --- a/utils/regression-analysis/src/main.rs +++ b/utils/regression-analysis/src/main.rs @@ -309,6 +309,8 @@ fn weights(kind: WeightsKind, input_file: PathBuf, output_file: PathBuf) { alloc, alloc_per_page, free, + free_range, + free_range_per_page, gr_gas_available, gr_message_id, gr_pay_program_rent, diff --git a/utils/wasm-gen/src/config/syscalls/param.rs b/utils/wasm-gen/src/config/syscalls/param.rs index f1bf90f1955..6e2352e41ae 100644 --- a/utils/wasm-gen/src/config/syscalls/param.rs +++ b/utils/wasm-gen/src/config/syscalls/param.rs @@ -97,6 +97,7 @@ impl Default for SyscallsParamsConfig { (ParamType::DelayBlockNumber, (0..=4).into()), (ParamType::Handler, (0..=100).into()), (ParamType::Free, (free_start..=free_end).into()), + (ParamType::FreeUpperBound, (0..=10).into()), (ParamType::Version, (1..=1).into()), ] .into_iter() diff --git a/utils/wasm-gen/src/generator/syscalls/invocator.rs b/utils/wasm-gen/src/generator/syscalls/invocator.rs index 06d0cb1bc4f..52318d268dd 100644 --- a/utils/wasm-gen/src/generator/syscalls/invocator.rs +++ b/utils/wasm-gen/src/generator/syscalls/invocator.rs @@ -43,6 +43,9 @@ pub(crate) enum ProcessedSyscallParams { Alloc { allowed_values: Option, }, + FreeUpperBound { + allowed_values: Option, + }, Value { value_type: ValueType, allowed_values: Option, @@ -84,6 +87,10 @@ pub(crate) fn process_syscall_params( ty: PtrType::SizedBufferStart { .. }, .. }) => ProcessedSyscallParams::MemoryArrayPtr, + ParamType::FreeUpperBound => { + let allowed_values = params_config.get_rule(¶m); + ProcessedSyscallParams::FreeUpperBound { allowed_values } + } ParamType::Ptr(_) => ProcessedSyscallParams::MemoryPtrValue, _ => ProcessedSyscallParams::Value { value_type: param.into(), @@ -167,6 +174,10 @@ impl ParamSetter { } } + /// Get value of the instruction. + /// + /// # Panics + /// Panics if the instruction is not `I32Const` or `I64Const`. fn get_value(&self) -> i64 { match self.0 { Instruction::I32Const(value) => value as i64, @@ -561,6 +572,23 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { setters.push(setter); } + ProcessedSyscallParams::FreeUpperBound { allowed_values } => { + let previous_param = setters + .last() + .expect("expected Free parameter before FreeUpperBound") + .as_i32() + .expect("referenced param should evaluate to I32Const"); + + let size = allowed_values + .expect("allowed_values should be set for FreeUpperBound") + .get_i32(self.unstructured)?; + + let value = previous_param.saturating_add(size); + + log::trace!(" ---- Add value - {}", value); + + setters.push(ParamSetter::new_i32(value)) + } } } @@ -638,12 +666,12 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { // Alloc syscall: returns u32::MAX (= -1i32) in case of error. -1 } - ParamType::Free => { - // Free syscall: returns 1 in case of error. + ParamType::Free | ParamType::FreeUpperBound => { + // free/free_range syscall: returns 1 in case of error. 1 } _ => { - unimplemented!("Only alloc and free are supported for now") + unimplemented!("Only alloc and free/free_range are supported for now") } }; diff --git a/utils/wasm-instrument/src/syscalls.rs b/utils/wasm-instrument/src/syscalls.rs index c00cace2543..3a3dd2a7cbe 100644 --- a/utils/wasm-instrument/src/syscalls.rs +++ b/utils/wasm-instrument/src/syscalls.rs @@ -93,6 +93,7 @@ pub enum SyscallName { // Hard under the hood calls, serving proper program execution Alloc, Free, + FreeRange, OutOfGas, // Miscellaneous @@ -120,6 +121,7 @@ impl SyscallName { SyscallName::OomPanic => "gr_oom_panic", SyscallName::Exit => "gr_exit", SyscallName::Free => "free", + SyscallName::FreeRange => "free_range", SyscallName::GasAvailable => "gr_gas_available", SyscallName::Leave => "gr_leave", SyscallName::MessageId => "gr_message_id", @@ -180,6 +182,7 @@ impl SyscallName { [ Self::Alloc, Self::Free, + Self::FreeRange, Self::Debug, Self::Panic, Self::OomPanic, @@ -242,6 +245,7 @@ impl SyscallName { match self { Self::Alloc => SyscallSignature::system([Alloc], [I32]), Self::Free => SyscallSignature::system([Free], [I32]), + Self::FreeRange => SyscallSignature::system([Free, FreeUpperBound], [I32]), Self::Debug => SyscallSignature::gr([ Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { length_param_idx: 1, @@ -610,16 +614,19 @@ impl SyscallName { /// belongs to. See [`PtrInfo`] and [`PtrType`] for more details. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ParamType { - Length, // i32 buffers length + Length, // i32 buffers size in memory Ptr(PtrInfo), // i32 pointer Gas, // i64 gas amount Offset, // i32 offset in the input buffer (message payload) + MessagePosition, // i32 message position DurationBlockNumber, // i32 duration in blocks DelayBlockNumber, // i32 delay in blocks Handler, // i32 handler number - Alloc, // i32 pages to alloc - Free, // i32 page number to free - Version, // i32 version number of exec settings + Alloc, // i32 alloc pages + Free, // i32 free page + // i32 free upper bound for use with free_range. Should be placed after Free in fn signature + FreeUpperBound, + Version, // i32 version number of exec settings } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]