Skip to content

Commit

Permalink
Make min/max funcs - non zero, reduce heap allocations in invocator
Browse files Browse the repository at this point in the history
  • Loading branch information
techraed committed Oct 4, 2023
1 parent e3e9bf4 commit 2ba529e
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 57 deletions.
5 changes: 3 additions & 2 deletions utils/wasm-gen/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
//! 1. From scratch by settings fields to corresponding values sometimes using
//! related to these fields builders. For example, wasm module configs:
//! ```rust
//! # use std::num::NonZeroUsize;
//! use gear_wasm_gen::*;
//! use arbitrary::{Arbitrary, Result, Unstructured};
//!
Expand All @@ -38,8 +39,8 @@
//! InstructionKind::Control,
//! ],
//! max_instructions: 100_000,
//! min_funcs: 15,
//! max_funcs: 30,
//! min_funcs: NonZeroUsize::new(15).unwrap(),
//! max_funcs: NonZeroUsize::new(30).unwrap(),
//! unreachable_enabled: true,
//! };
//! let arbitrary = ArbitraryParams::arbitrary(u)?;
Expand Down
13 changes: 9 additions & 4 deletions utils/wasm-gen/src/config/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
//! can be arbitrary, but some must be constantly set. That's implemented with [`ArbitraryParams`]
//! and [`ConstantParams`].
use std::num::NonZeroUsize;

use arbitrary::{Arbitrary, Result, Unstructured};
pub use wasm_smith::InstructionKind;
use wasm_smith::{InstructionKind::*, InstructionKinds, SwarmConfig};
Expand Down Expand Up @@ -88,6 +90,9 @@ impl From<(SelectableParams, ArbitraryParams)> for WasmModuleConfig {
unreachable_enabled,
} = selectable_params;

let min_funcs = min_funcs.get();
let max_funcs = max_funcs.get();

let ArbitraryParams {
available_imports,
canonicalize_nans,
Expand Down Expand Up @@ -352,10 +357,10 @@ pub struct SelectableParams {
pub max_instructions: usize,
/// Minimum amount of functions `wasm-gen` will insert
/// into generated wasm.
pub min_funcs: usize,
pub min_funcs: NonZeroUsize,
/// Maximum amount of functions `wasm-gen` will insert
/// into generated wasm.
pub max_funcs: usize,
pub max_funcs: NonZeroUsize,
/// Flag signalizing whether `unreachable` instruction
/// must be used or not.
pub unreachable_enabled: bool,
Expand All @@ -369,8 +374,8 @@ impl Default for SelectableParams {
Numeric, Reference, Parametric, Variable, Table, Memory, Control,
],
max_instructions: 500,
min_funcs: 3,
max_funcs: 5,
min_funcs: NonZeroUsize::new(3).expect("from non zero value; qed."),
max_funcs: NonZeroUsize::new(5).expect("from non zero value; qed."),
unreachable_enabled: true,
}
}
Expand Down
39 changes: 33 additions & 6 deletions utils/wasm-gen/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
use crate::{utils, GearWasmGeneratorConfig, WasmModule};
use arbitrary::{Result, Unstructured};
use gear_wasm_instrument::parity_wasm::elements::Module;
use std::collections::HashSet;
use std::{collections::HashSet, ops::RangeInclusive};

mod entry_points;
mod memory;
Expand Down Expand Up @@ -216,15 +216,17 @@ struct CallIndexes {
///
/// These are indexes of functions which aren't generated from
/// `wasm-smith` but from the current crate generators. All gear
/// entry points ([`EntryPointsGenerator`]) and custom reservation send
/// function (generated in [`SysCallsImportsGenerator`]) are considered
/// to be "custom" functions.
/// entry points ([`EntryPointsGenerator`]) and custom precuse syscalls
/// (generated in [`SysCallsImportsGenerator`]) are considered to be
/// "custom" functions.
///
/// Separating "pre-defined" functions from newly generated ones is important
/// when syscalls invocator inserts calls of generated syscalls. For example,
/// calls must not be inserted in the custom function, which perofrms `gr_reservation_send`,
/// not to pollute it's internal instructions structure which is defined such that
/// semantically correct `gr_reservation_send` call is performed.
///
/// Same immutability is actual for gear exports to keep them as simple as possible.
custom_funcs: HashSet<usize>,
}

Expand All @@ -250,8 +252,24 @@ impl CallIndexes {
self.inner.get(handle_idx).copied()
}

fn is_custom_func(&self, idx: usize) -> bool {
self.custom_funcs.contains(&idx)
fn predefined_funcs_indexes(&self) -> RangeInclusive<usize> {
let last = if let Some(first_custom_func_idx) = self.custom_funcs.iter().min() {
// Take last predefined func idx
//
// Subtraction is safe, because by config it's guaranteed
// that there's at least one internal function from `wasm-smith`.
// So, if there's only one predefined function, then first idx
// of a custom function is 1.
first_custom_func_idx - 1
} else {
self.inner
.iter()
.filter_map(FunctionIndex::internal_func_idx)
.max()
.expect("at least 1 func is generated by config definition") as usize
};

0..=last
}

fn len(&self) -> usize {
Expand Down Expand Up @@ -282,6 +300,15 @@ enum FunctionIndex {
Func(u32),
}

impl FunctionIndex {
fn internal_func_idx(&self) -> Option<u32> {
match self {
FunctionIndex::Func(idx) => Some(*idx),
_ => None,
}
}
}

/// Frozen gear wasm generator.
///
/// Instantce of this generator signals, that some gear wasm generator
Expand Down
10 changes: 5 additions & 5 deletions utils/wasm-gen/src/generator/syscalls/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,11 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> {
syscall: SysCallName,
is_loose: bool,
) -> Result<u32> {
use InvocableSysCall::*;

let invocable_syscall = is_loose
.then_some(Loose(syscall))
.unwrap_or(Precise(syscall));
let invocable_syscall = if is_loose {
InvocableSysCall::Loose(syscall)
} else {
InvocableSysCall::Precise(syscall)
};
let syscall_amount_range = self.config.injection_amounts(invocable_syscall);

self.unstructured.int_in_range(syscall_amount_range)
Expand Down
53 changes: 20 additions & 33 deletions utils/wasm-gen/src/generator/syscalls/invocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,24 +201,7 @@ impl<'a, 'b> SysCallsInvocator<'a, 'b> {
self.unstructured.len()
);

let code_funcs = self.module.count_code_funcs();
let insert_into_funcs: Vec<_> = (0..code_funcs)
.filter(|idx| !self.call_indexes.is_custom_func(*idx))
.collect();

let syscalls_to_insert =
self.syscalls_imports
.clone()
.into_iter()
.flat_map(|(syscall, (amount, _))| {
iter::repeat(syscall)
.take(amount as usize)
.collect::<Vec<_>>()
});

let insertion_mapping =
self.build_syscalls_insertion_mapping(syscalls_to_insert, &insert_into_funcs)?;

let insertion_mapping = self.build_syscalls_insertion_mapping()?;
for (insert_into_fn, syscalls) in insertion_mapping {
self.insert_syscalls_into_fn(insert_into_fn, syscalls)?;
}
Expand All @@ -234,24 +217,28 @@ impl<'a, 'b> SysCallsInvocator<'a, 'b> {
/// Distributes provided syscalls among provided function ids.
///
/// Returns mapping `func_id` <-> `syscalls which should be inserted into func_id`.
fn build_syscalls_insertion_mapping<I>(
fn build_syscalls_insertion_mapping(
&mut self,
syscalls: I,
insert_into_funcs: &[usize],
) -> Result<BTreeMap<usize, Vec<InvocableSysCall>>>
where
I: Iterator<Item = InvocableSysCall>,
{
) -> Result<BTreeMap<usize, Vec<InvocableSysCall>>> {
let insert_into_funcs = self.call_indexes.predefined_funcs_indexes();
let syscalls = self
.syscalls_imports
.clone()
.into_iter()
.map(|(syscall, (amount, _))| (syscall, amount));

let mut insertion_mapping: BTreeMap<_, Vec<_>> = BTreeMap::new();
for syscall in syscalls {
let insert_into = *self.unstructured.choose(insert_into_funcs)?;
for (syscall, amount) in syscalls {
for _ in 0..amount {
let insert_into = self.unstructured.int_in_range(insert_into_funcs.clone())?;

match insertion_mapping.entry(insert_into) {
Entry::Occupied(mut entry) => {
entry.get_mut().push(syscall);
}
Entry::Vacant(entry) => {
entry.insert(vec![syscall]);
match insertion_mapping.entry(insert_into) {
Entry::Occupied(mut entry) => {
entry.get_mut().push(syscall);
}
Entry::Vacant(entry) => {
entry.insert(vec![syscall]);
}
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions utils/wasm-gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,5 @@ pub fn generate_gear_program_module(
}

// TODO
// 2. clean-up in SysCallsImportsGenerator
// 3. clean-up after 3242, 3196
// 4. Check all other code
// 5. Check logs after merging @mertwole's changes. Make it more clear.
// 3. clean-up invocator
// 4. Check logs.
6 changes: 3 additions & 3 deletions utils/wasm-gen/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use gear_wasm_instrument::{
};
use proptest::prelude::*;
use rand::{rngs::SmallRng, RngCore, SeedableRng};
use std::mem;
use std::{mem, num::NonZeroUsize};

const UNSTRUCTURED_SIZE: usize = 1_000_000;

Expand Down Expand Up @@ -347,8 +347,8 @@ fn execute_wasm_with_custom_configs(
call_indirect_enabled: false,
allowed_instructions: vec![],
max_instructions: 0,
min_funcs: 1,
max_funcs: 1,
min_funcs: NonZeroUsize::new(1).unwrap(),
max_funcs: NonZeroUsize::new(1).unwrap(),
unreachable_enabled: true,
},
);
Expand Down

0 comments on commit 2ba529e

Please sign in to comment.