Skip to content

Commit aec41ff

Browse files
authored
Drop CodeView from Wasmi's executor and VmState (#2015)
* add WasmFeatures to StoreInner * add WasmFuncEntity::func_entry_ptr getter * add CodeMap::entry getter * no longer use CodeView in execution handlers * no longer use CodeView in executor setup * no longer use CodeView in engine * remove CodeView type * remove CodeMap::view getter * remove WasmFeatures from CodeMap now WasmFeatures from the Store or the ModueHeader is used instead. * update CodeMap init * update comment in host call test
1 parent 3bfbd48 commit aec41ff

9 files changed

Lines changed: 66 additions & 180 deletions

File tree

crates/wasmi/src/engine/code_map/mod.rs

Lines changed: 17 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use self::utils::SmallByteSlice;
1414
use super::ValidatingFuncTranslator;
1515
use super::{FuncToValidate, FuncTranslationDriver, FuncTranslator, TranslationError};
1616
use crate::{
17-
Config,
1817
Error,
1918
TrapCode,
2019
core::{Fuel, FuelCostsProvider, hint},
@@ -58,26 +57,21 @@ const LEN_BUCKET0: u64 = 1 << LEN_BUCKET0_LOG2;
5857
const MAX_BUCKETS: usize = Funcs::required_buckets_for_len(MAX_FUNCS);
5958

6059
/// A data structure to store and manage [`FuncEntry`] definitions.
61-
#[derive(Debug)]
60+
///
61+
/// # Note
62+
///
63+
/// The [`WasmFeatures`] required to compile a [`FuncEntry`] are not stored here but in the
64+
/// [`StoreInner`](crate::store::StoreInner) driving the compilation, where the executor
65+
/// reaches them without a dependent load.
66+
#[derive(Debug, Default)]
6267
pub struct CodeMap {
6368
/// The append-only, lock-free-readable storage for all [`FuncEntry`] definitions.
6469
funcs: Funcs,
6570
/// Serializes concurrent writers ([`Self::alloc_funcs`]); readers never take this lock.
6671
alloc_lock: Mutex<()>,
67-
/// Shared Wasm features across all [`FuncEntry`] definitions within the [`CodeMap`].
68-
features: WasmFeatures,
6972
}
7073

7174
impl CodeMap {
72-
/// Creates a new [`CodeMap`].
73-
pub fn new(config: &Config) -> Self {
74-
Self {
75-
funcs: Funcs::default(),
76-
alloc_lock: Mutex::new(()),
77-
features: config.wasm_features(),
78-
}
79-
}
80-
8175
/// Allocates `amount` new uninitialized [`EngineFunc`] to the [`CodeMap`].
8276
///
8377
/// # Note
@@ -95,6 +89,14 @@ impl CodeMap {
9589
}
9690
}
9791

92+
/// Returns a shared reference to the [`FuncEntry`] of `func` if published.
93+
///
94+
/// Returns `None` if `func` is not (yet) published to this [`CodeMap`].
95+
#[inline]
96+
pub fn entry(&self, func: EngineFunc) -> Option<&FuncEntry> {
97+
self.funcs.get(func)
98+
}
99+
98100
/// Initializes the [`EngineFunc`] with its [`CompiledFuncEntry`].
99101
///
100102
/// # Panics
@@ -134,110 +136,6 @@ impl CodeMap {
134136
func_to_validate,
135137
));
136138
}
137-
138-
/// Returns a cheap, lock-free [`CodeView`] snapshot of this [`CodeMap`].
139-
///
140-
/// # Note
141-
///
142-
/// The snapshot borrows `self` and caches the currently published function count; it is used by
143-
/// the executor to resolve calls without locking or per-call atomics.
144-
#[inline]
145-
pub fn view(&self) -> CodeView<'_> {
146-
// Note: a single `Acquire` load establishes the happens-before for every bucket published before
147-
// this length; subsequent per-call reads through the snapshot are plain (non-atomic).
148-
let len_funcs = self.funcs.len_funcs.load(Ordering::Acquire);
149-
CodeView {
150-
code_map: self,
151-
len_funcs,
152-
}
153-
}
154-
}
155-
156-
/// A cheap, lock-free, stale-but-valid snapshot of a [`CodeMap`].
157-
///
158-
/// Borrows the source [`CodeMap`] and caches the number of published functions, so the executor
159-
/// can resolve functions without locking and without any atomic load on the per-call path.
160-
///
161-
/// # Note
162-
///
163-
/// - Represents a potentially stale view: functions appended after the snapshot are not visible
164-
/// until [`CodeView::refresh`] is called. Since [`Funcs`] is append-only and pointer-stable, a
165-
/// stale view is never invalid, only outdated.
166-
/// - Holding `&CodeMap` is sound under both Stacked and Tree Borrows because no `&mut CodeMap`
167-
/// (or `&mut Funcs`) is ever formed while a view is alive: all publication goes through interior
168-
/// mutability under the writer lock.
169-
/// - Upon execution an [`Engine`] derives a [`CodeView`] of the current [`CodeMap`] state and uses
170-
/// it to drive call-based executions without taking the writer lock. After a host function call
171-
/// the view must be [`refresh`](CodeView::refresh)ed, since the host may have appended functions
172-
/// (e.g. by compiling and instantiating new modules) that the resuming Wasm can reach.
173-
///
174-
/// [`Engine`]: crate::Engine
175-
#[derive(Copy, Clone)]
176-
pub struct CodeView<'a> {
177-
/// The source [`CodeMap`]. The bucket-array base address and `features` are stable for its
178-
/// lifetime; only the published function count grows (tracked by `len_funcs`).
179-
code_map: &'a CodeMap,
180-
/// Cached number of published [`FuncEntry`] definitions; bounds visibility.
181-
///
182-
/// # Note
183-
///
184-
/// This is only updated by [`CodeView::refresh`], so the per-call read path performs no atomic load.
185-
len_funcs: usize,
186-
}
187-
188-
impl fmt::Debug for CodeView<'_> {
189-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190-
f.debug_struct("CodeView")
191-
.field("len_funcs", &self.len_funcs)
192-
.finish_non_exhaustive()
193-
}
194-
}
195-
196-
impl<'a> CodeView<'a> {
197-
/// Re-materializes the snapshot to reflect functions published since it was last (re)created.
198-
///
199-
/// Must be called after a host function call: the host may have compiled and/or instantiated
200-
/// new Wasm modules (appending to the [`CodeMap`]) that the resuming Wasm code can then reach.
201-
#[inline]
202-
pub fn refresh(&mut self) {
203-
self.len_funcs = self.code_map.funcs.len_funcs.load(Ordering::Acquire);
204-
}
205-
206-
/// Returns a shared reference to the [`FuncEntry`] of `func` if visible in this snapshot.
207-
///
208-
/// Returns `None` if `func` is not (yet) visible to this snapshot.
209-
#[inline]
210-
pub fn entry(&self, func: EngineFunc) -> Option<&'a FuncEntry> {
211-
// Safety: `len_funcs` is loaded via acquire upon creation of `self` so that `buckets` are published.
212-
unsafe { self.code_map.funcs.get_within(func, self.len_funcs) }
213-
}
214-
215-
/// Returns the [`CompiledFuncRef`] of `func`, compiling it lazily if still uncompiled.
216-
///
217-
/// Returns `None` if the `func` index is out of bounds for `self`.
218-
///
219-
/// # Errors
220-
///
221-
/// - If translation or Wasm validation of `func` failed.
222-
/// - If `fuel` ran out in case fuel consumption is enabled.
223-
#[track_caller]
224-
#[inline]
225-
pub fn get_or_compile(
226-
&self,
227-
fuel: Option<&mut Fuel>,
228-
func: EngineFunc,
229-
) -> Result<Option<CompiledFuncRef<'a>>, Error> {
230-
let Some(entry) = self.entry(func) else {
231-
return Ok(None);
232-
};
233-
let compiled = entry.get_or_compile(fuel, &self.code_map.features)?;
234-
Ok(Some(compiled))
235-
}
236-
237-
/// Returns the [`WasmFeatures`] of the underlying [`CodeMap`].
238-
pub fn features(&self) -> &WasmFeatures {
239-
&self.code_map.features
240-
}
241139
}
242140

243141
/// An append-only collection for [`FuncEntry`] definitions.
@@ -250,8 +148,8 @@ pub struct Funcs {
250148
/// - The first `required_buckets_for_len(len_funcs)` slots are `Some` and, once published,
251149
/// are never written or moved again.
252150
/// - The `buckets` array lives behind an [`UnsafeCell`] so that new buckets can be
253-
/// published through a shared `&Funcs` (no `&mut Funcs` is ever formed), which is what lets a
254-
/// [`CodeView`] snapshot read `buckets` lock-free and without atomics on the per-call path.
151+
/// published through a shared `&Funcs` (no `&mut Funcs` is ever formed), which is what lets
152+
/// readers resolve a [`FuncEntry`] lock-free.
255153
buckets: UnsafeCell<[Option<RawFuncsBucket>; MAX_BUCKETS]>,
256154
/// The number of [`FuncEntry`] definitions published across all `buckets`.
257155
///

crates/wasmi/src/engine/executor/handler/func.rs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ use crate::{
44
Instance,
55
Store,
66
engine::{
7-
CodeView,
8-
EngineFunc,
7+
FuncEntryPtr,
98
LiftFromCells,
109
LowerToCells,
1110
executor::handler::{
@@ -23,7 +22,6 @@ use core::marker::PhantomData;
2322
pub struct WasmFuncCall<'a, T, State> {
2423
store: &'a mut Store<T>,
2524
stack: &'a mut Stack,
26-
code: CodeView<'a>,
2725
callee_ip: Ip,
2826
callee_sp: Sp,
2927
instance: Inst,
@@ -38,7 +36,6 @@ impl<'a, T, State> WasmFuncCall<'a, T, State> {
3836
WasmFuncCall {
3937
store: self.store,
4038
stack: self.stack,
41-
code: self.code,
4239
callee_ip: self.callee_ip,
4340
callee_sp: self.callee_sp,
4441
instance: self.instance,
@@ -110,7 +107,7 @@ impl<'a, T, State: state::Execute> WasmFuncCall<'a, T, State> {
110107
fn execute_until_done(&mut self) -> Result<Sp, ExecutionOutcome> {
111108
let store = self.store.prune();
112109
let (mem0, mem0_len) = utils::extract_mem0(store, self.instance);
113-
let mut state = VmState::new(store, self.stack, self.code);
110+
let mut state = VmState::new(store, self.stack);
114111
execute_until_done(
115112
&mut state,
116113
self.callee_ip,
@@ -157,14 +154,15 @@ impl<'a, T> WasmFuncCall<'a, T, state::Done> {
157154

158155
pub fn init_wasm_func_call<'a, T>(
159156
store: &'a mut Store<T>,
160-
code: CodeView<'a>,
161157
stack: &'a mut Stack,
162-
func: EngineFunc,
158+
func_entry: FuncEntryPtr,
163159
instance: Instance,
164160
) -> Result<WasmFuncCall<'a, T, state::Uninit>, Error> {
165-
let Some(compiled_func) = code.get_or_compile(Some(store.inner.fuel_mut()), func)? else {
166-
panic!("missing function entry at: {func:?}")
167-
};
161+
// SAFETY: `func_entry` stems from a `WasmFuncEntity` owned by `store`, thus the engine
162+
// owning the `FuncEntry` outlives this call.
163+
let func_entry = unsafe { func_entry.get() };
164+
let (fuel, features) = store.inner.fuel_and_features();
165+
let compiled_func = func_entry.get_or_compile(Some(fuel), features)?;
168166
let callee_ip = Ip::from(compiled_func.ops());
169167
let len_local_slots = compiled_func.len_local_slots();
170168
let len_stack_slots = compiled_func.len_stack_slots();
@@ -189,7 +187,6 @@ pub fn init_wasm_func_call<'a, T>(
189187
Ok(WasmFuncCall {
190188
store,
191189
stack,
192-
code,
193190
callee_ip,
194191
callee_sp,
195192
instance,
@@ -202,14 +199,12 @@ pub fn init_wasm_func_call<'a, T>(
202199

203200
pub fn resume_wasm_func_call<'a, T>(
204201
store: &'a mut Store<T>,
205-
code: CodeView<'a>,
206202
stack: &'a mut Stack,
207203
) -> Result<WasmFuncCall<'a, T, state::Resumed>, Error> {
208204
let (callee_ip, callee_sp, instance, ireg, freg32, freg64) = stack.restore_frame();
209205
Ok(WasmFuncCall {
210206
store,
211207
stack,
212-
code,
213208
callee_ip,
214209
callee_sp,
215210
instance,

crates/wasmi/src/engine/executor/handler/state.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use crate::{
1212
CellError,
1313
CellsReader,
1414
CellsWriter,
15-
CodeView,
1615
InOutParams,
1716
LoadFromCellsByValue,
1817
StoreToCells,
@@ -33,16 +32,14 @@ use core::{cmp, marker::PhantomData, mem, ops, ptr, slice};
3332
pub struct VmState<'vm> {
3433
pub store: &'vm mut PrunedStore,
3534
pub stack: &'vm mut Stack,
36-
pub code: CodeView<'vm>,
3735
done_reason: Option<DoneReason>,
3836
}
3937

4038
impl<'vm> VmState<'vm> {
41-
pub fn new(store: &'vm mut PrunedStore, stack: &'vm mut Stack, code: CodeView<'vm>) -> Self {
39+
pub fn new(store: &'vm mut PrunedStore, stack: &'vm mut Stack) -> Self {
4240
Self {
4341
store,
4442
stack,
45-
code,
4643
done_reason: None,
4744
}
4845
}

crates/wasmi/src/engine/executor/handler/utils.rs

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ use crate::{
2121
ShiftAmount,
2222
},
2323
engine::{
24-
CodeView,
2524
FuncEntry,
2625
InOutParams,
2726
executor::{
@@ -67,9 +66,8 @@ pub fn compile_or_get_func_entry(
6766
state: &mut VmState,
6867
func: &FuncEntry,
6968
) -> Result<(Ip, u16, u16), Error> {
70-
let fuel_mut = state.store.inner_mut().fuel_mut();
71-
let features = state.code.features();
72-
let compiled_func = func.get_or_compile(Some(fuel_mut), features)?;
69+
let (fuel, features) = state.store.inner_mut().fuel_and_features();
70+
let compiled_func = func.get_or_compile(Some(fuel), features)?;
7371
let ip = Ip::from(compiled_func.ops());
7472
let len_local_slots = compiled_func.len_local_slots();
7573
let len_stack_slots = compiled_func.len_stack_slots();
@@ -822,14 +820,12 @@ pub fn return_call_func_entry(
822820
///
823821
/// # Note
824822
///
825-
/// Takes `store` and `code` instead of the whole [`VmState`] since `inout` already
826-
/// borrows its [`Stack`].
823+
/// Takes `store` instead of the whole [`VmState`] since `inout` already borrows its [`Stack`].
827824
///
828825
/// [`Stack`]: crate::engine::executor::Stack
829826
#[inline]
830827
fn invoke_host(
831828
store: &mut PrunedStore,
832-
code: &mut CodeView,
833829
trampoline: Trampoline,
834830
instance: Option<Inst>,
835831
inout: InOutParams<'_>,
@@ -844,7 +840,6 @@ fn invoke_host(
844840
)
845841
},
846842
}
847-
code.refresh();
848843
Ok(())
849844
}
850845

@@ -863,14 +858,7 @@ pub fn call_host(
863858
.stack
864859
.prepare_host_frame(caller_ip, params, host_func.len_result_cells())
865860
.into_control()?;
866-
if let Err(error) = invoke_host(
867-
state.store,
868-
&mut state.code,
869-
trampoline,
870-
instance,
871-
inout,
872-
call_hooks,
873-
) {
861+
if let Err(error) = invoke_host(state.store, trampoline, instance, inout, call_hooks) {
874862
done!(state, DoneReason::host_error(error, func, params.span()))
875863
}
876864
Control::Continue(sp)
@@ -891,7 +879,6 @@ pub fn return_call_host(
891879
.into_control()?;
892880
if let Err(error) = invoke_host(
893881
state.store,
894-
&mut state.code,
895882
trampoline,
896883
Some(instance),
897884
inout,

0 commit comments

Comments
 (0)