Skip to content

Commit 63d80fc

Browse files
alexcrichtonabrown
andauthored
Remove the need to have a Store for an InstancePre (#5683)
* Remove the need to have a `Store` for an `InstancePre` This commit relaxes a requirement of the `InstancePre` API, notably its construction via `Linker::instantiate_pre`. Previously this function required a `Store<T>` to be present to be able to perform type-checking on the contents of the linker, and now this requirement has been removed. Items stored within a linker are either a `HostFunc`, which has type information inside of it, or an `Extern`, which doesn't have type information inside of it. Due to the usage of `Extern` this is why a `Store` was required during the `InstancePre` construction process, it's used to extract the type of an `Extern`. This commit implements a solution where the type information of an `Extern` is stored alongside the `Extern` itself, meaning that the `InstancePre` construction process no longer requires a `Store<T>`. One caveat of this implementation is that some items, such as tables and memories, technically have a "dynamic type" where during type checking their current size is consulted to match against the minimum size required of an import. This no longer works when using `Linker::instantiate_pre` as the current size used is the one when it was inserted into the linker rather than the one available at instantiation time. It's hoped, however, that this is a relatively esoteric use case that doesn't impact many real-world users. Additionally note that this is an API-breaking change. Not only is the `Store` argument removed from `Linker::instantiate_pre`, but some other methods such as `Linker::define` grew a `Store` argument as the type needs to be extracted when an item is inserted into a linker. Closes #5675 * Fix the C API * Fix benchmark compilation * Add C API docs * Update crates/wasmtime/src/linker.rs Co-authored-by: Andrew Brown <[email protected]> --------- Co-authored-by: Andrew Brown <[email protected]>
1 parent f5f517e commit 63d80fc

File tree

13 files changed

+245
-227
lines changed

13 files changed

+245
-227
lines changed

benches/instantiation.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ fn bench_sequential(c: &mut Criterion, path: &Path) {
4646
let mut linker = Linker::new(&engine);
4747
wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap();
4848
let pre = linker
49-
.instantiate_pre(&mut store(&engine), &module)
49+
.instantiate_pre(&module)
5050
.expect("failed to pre-instantiate");
5151
(engine, pre)
5252
});
@@ -77,7 +77,7 @@ fn bench_parallel(c: &mut Criterion, path: &Path) {
7777
wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap();
7878
let pre = Arc::new(
7979
linker
80-
.instantiate_pre(&mut store(&engine), &module)
80+
.instantiate_pre(&module)
8181
.expect("failed to pre-instantiate"),
8282
);
8383
(engine, pre)

crates/c-api/include/wasmtime/linker.h

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker,
5959
* \brief Defines a new item in this linker.
6060
*
6161
* \param linker the linker the name is being defined in.
62+
* \param store the store that the `item` is owned by.
6263
* \param module the module name the item is defined under.
6364
* \param module_len the byte length of `module`
6465
* \param name the field name the item is defined under
@@ -73,6 +74,7 @@ WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker,
7374
*/
7475
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define(
7576
wasmtime_linker_t *linker,
77+
wasmtime_context_t *store,
7678
const char *module,
7779
size_t module_len,
7880
const char *name,

crates/c-api/src/linker.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
bad_utf8, handle_result, wasm_engine_t, wasm_functype_t, wasm_trap_t, wasmtime_error_t,
3-
wasmtime_extern_t, wasmtime_module_t, CStoreContextMut,
3+
wasmtime_extern_t, wasmtime_module_t, CStoreContext, CStoreContextMut,
44
};
55
use std::ffi::c_void;
66
use std::mem::MaybeUninit;
@@ -41,6 +41,7 @@ macro_rules! to_str {
4141
#[no_mangle]
4242
pub unsafe extern "C" fn wasmtime_linker_define(
4343
linker: &mut wasmtime_linker_t,
44+
store: CStoreContext<'_>,
4445
module: *const u8,
4546
module_len: usize,
4647
name: *const u8,
@@ -51,7 +52,7 @@ pub unsafe extern "C" fn wasmtime_linker_define(
5152
let module = to_str!(module, module_len);
5253
let name = to_str!(name, name_len);
5354
let item = item.to_extern();
54-
handle_result(linker.define(module, name, item), |_linker| ())
55+
handle_result(linker.define(&store, module, name, item), |_linker| ())
5556
}
5657

5758
#[no_mangle]

crates/fuzzing/src/oracles.rs

+57-70
Original file line numberDiff line numberDiff line change
@@ -544,45 +544,40 @@ pub fn table_ops(
544544
// test case.
545545
const MAX_GCS: usize = 5;
546546

547-
linker
548-
.define(
549-
"",
550-
"gc",
551-
// NB: use `Func::new` so that this can still compile on the old x86
552-
// backend, where `IntoFunc` isn't implemented for multi-value
553-
// returns.
554-
Func::new(
555-
&mut store,
556-
FuncType::new(
557-
vec![],
558-
vec![ValType::ExternRef, ValType::ExternRef, ValType::ExternRef],
559-
),
560-
{
561-
let num_dropped = num_dropped.clone();
562-
let expected_drops = expected_drops.clone();
563-
let num_gcs = num_gcs.clone();
564-
move |mut caller: Caller<'_, StoreLimits>, _params, results| {
565-
log::info!("table_ops: GC");
566-
if num_gcs.fetch_add(1, SeqCst) < MAX_GCS {
567-
caller.gc();
568-
}
569-
570-
let a = ExternRef::new(CountDrops(num_dropped.clone()));
571-
let b = ExternRef::new(CountDrops(num_dropped.clone()));
572-
let c = ExternRef::new(CountDrops(num_dropped.clone()));
573-
574-
log::info!("table_ops: make_refs() -> ({:p}, {:p}, {:p})", a, b, c);
575-
576-
expected_drops.fetch_add(3, SeqCst);
577-
results[0] = Some(a).into();
578-
results[1] = Some(b).into();
579-
results[2] = Some(c).into();
580-
Ok(())
581-
}
582-
},
583-
),
584-
)
585-
.unwrap();
547+
// NB: use `Func::new` so that this can still compile on the old x86
548+
// backend, where `IntoFunc` isn't implemented for multi-value
549+
// returns.
550+
let func = Func::new(
551+
&mut store,
552+
FuncType::new(
553+
vec![],
554+
vec![ValType::ExternRef, ValType::ExternRef, ValType::ExternRef],
555+
),
556+
{
557+
let num_dropped = num_dropped.clone();
558+
let expected_drops = expected_drops.clone();
559+
let num_gcs = num_gcs.clone();
560+
move |mut caller: Caller<'_, StoreLimits>, _params, results| {
561+
log::info!("table_ops: GC");
562+
if num_gcs.fetch_add(1, SeqCst) < MAX_GCS {
563+
caller.gc();
564+
}
565+
566+
let a = ExternRef::new(CountDrops(num_dropped.clone()));
567+
let b = ExternRef::new(CountDrops(num_dropped.clone()));
568+
let c = ExternRef::new(CountDrops(num_dropped.clone()));
569+
570+
log::info!("table_ops: make_refs() -> ({:p}, {:p}, {:p})", a, b, c);
571+
572+
expected_drops.fetch_add(3, SeqCst);
573+
results[0] = Some(a).into();
574+
results[1] = Some(b).into();
575+
results[2] = Some(c).into();
576+
Ok(())
577+
}
578+
},
579+
);
580+
linker.define(&store, "", "gc", func).unwrap();
586581

587582
linker
588583
.func_wrap("", "take_refs", {
@@ -624,37 +619,29 @@ pub fn table_ops(
624619
})
625620
.unwrap();
626621

627-
linker
628-
.define(
629-
"",
630-
"make_refs",
631-
// NB: use `Func::new` so that this can still compile on the old
632-
// x86 backend, where `IntoFunc` isn't implemented for
633-
// multi-value returns.
634-
Func::new(
635-
&mut store,
636-
FuncType::new(
637-
vec![],
638-
vec![ValType::ExternRef, ValType::ExternRef, ValType::ExternRef],
639-
),
640-
{
641-
let num_dropped = num_dropped.clone();
642-
let expected_drops = expected_drops.clone();
643-
move |_caller, _params, results| {
644-
log::info!("table_ops: make_refs");
645-
expected_drops.fetch_add(3, SeqCst);
646-
results[0] =
647-
Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
648-
results[1] =
649-
Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
650-
results[2] =
651-
Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
652-
Ok(())
653-
}
654-
},
655-
),
656-
)
657-
.unwrap();
622+
// NB: use `Func::new` so that this can still compile on the old
623+
// x86 backend, where `IntoFunc` isn't implemented for
624+
// multi-value returns.
625+
let func = Func::new(
626+
&mut store,
627+
FuncType::new(
628+
vec![],
629+
vec![ValType::ExternRef, ValType::ExternRef, ValType::ExternRef],
630+
),
631+
{
632+
let num_dropped = num_dropped.clone();
633+
let expected_drops = expected_drops.clone();
634+
move |_caller, _params, results| {
635+
log::info!("table_ops: make_refs");
636+
expected_drops.fetch_add(3, SeqCst);
637+
results[0] = Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
638+
results[1] = Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
639+
results[2] = Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
640+
Ok(())
641+
}
642+
},
643+
);
644+
linker.define(&store, "", "make_refs", func).unwrap();
658645

659646
let instance = linker.instantiate(&mut store, &module).unwrap();
660647
let run = instance.get_func(&mut store, "run").unwrap();

crates/fuzzing/src/oracles/dummy.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@ pub fn dummy_linker<'module, T>(store: &mut Store<T>, module: &Module) -> Result
88
let mut linker = Linker::new(store.engine());
99
linker.allow_shadowing(true);
1010
for import in module.imports() {
11+
let extern_ = dummy_extern(store, import.ty())?;
1112
linker
12-
.define(
13-
import.module(),
14-
import.name(),
15-
dummy_extern(store, import.ty())?,
16-
)
13+
.define(&store, import.module(), import.name(), extern_)
1714
.unwrap();
1815
}
1916
Ok(linker)

crates/wasmtime/src/component/instance.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::component::func::HostFunc;
22
use crate::component::{Component, ComponentNamedList, Func, Lift, Lower, TypedFunc};
33
use crate::instance::OwnedImports;
4+
use crate::linker::DefinitionType;
45
use crate::store::{StoreOpaque, Stored};
56
use crate::{AsContextMut, Module, StoreContextMut};
67
use anyhow::{anyhow, Context, Result};
@@ -439,13 +440,13 @@ impl<'a> Instantiator<'a> {
439440
}
440441

441442
let val = unsafe { crate::Extern::from_wasmtime_export(export, store) };
443+
let ty = DefinitionType::from(store, &val);
442444
crate::types::matching::MatchCx {
443-
store,
444445
engine: store.engine(),
445446
signatures: module.signatures(),
446447
types: module.types(),
447448
}
448-
.extern_(&expected, &val)
449+
.definition(&expected, &ty)
449450
.expect("unexpected typecheck failure");
450451
}
451452
}

crates/wasmtime/src/externals.rs

+2-12
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,6 @@ impl Extern {
138138
Extern::Table(t) => store.store_data().contains(t.0),
139139
}
140140
}
141-
142-
pub(crate) fn desc(&self) -> &'static str {
143-
match self {
144-
Extern::Func(_) => "function",
145-
Extern::Table(_) => "table",
146-
Extern::Memory(_) => "memory",
147-
Extern::SharedMemory(_) => "shared memory",
148-
Extern::Global(_) => "global",
149-
}
150-
}
151141
}
152142

153143
impl From<Func> for Extern {
@@ -233,8 +223,8 @@ impl Global {
233223
/// )?;
234224
///
235225
/// let mut linker = Linker::new(&engine);
236-
/// linker.define("", "i32-const", i32_const)?;
237-
/// linker.define("", "f64-mut", f64_mut)?;
226+
/// linker.define(&store, "", "i32-const", i32_const)?;
227+
/// linker.define(&store, "", "f64-mut", f64_mut)?;
238228
///
239229
/// let instance = linker.instantiate(&mut store, &module)?;
240230
/// // ...

crates/wasmtime/src/instance.rs

+8-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::linker::Definition;
1+
use crate::linker::{Definition, DefinitionType};
22
use crate::store::{InstanceId, StoreOpaque, Stored};
33
use crate::types::matching;
44
use crate::{
@@ -157,7 +157,10 @@ impl Instance {
157157
bail!("cross-`Store` instantiation is not currently supported");
158158
}
159159
}
160-
typecheck(store, module, imports, |cx, ty, item| cx.extern_(ty, item))?;
160+
typecheck(module, imports, |cx, ty, item| {
161+
let item = DefinitionType::from(store, item);
162+
cx.definition(ty, &item)
163+
})?;
161164
let mut owned_imports = OwnedImports::new(module);
162165
for import in imports {
163166
owned_imports.push(import, store);
@@ -679,19 +682,8 @@ impl<T> InstancePre<T> {
679682
/// This method is unsafe as the `T` of the `InstancePre<T>` is not
680683
/// guaranteed to be the same as the `T` within the `Store`, the caller must
681684
/// verify that.
682-
pub(crate) unsafe fn new(
683-
store: &mut StoreOpaque,
684-
module: &Module,
685-
items: Vec<Definition>,
686-
) -> Result<InstancePre<T>> {
687-
for import in items.iter() {
688-
if !import.comes_from_same_store(store) {
689-
bail!("cross-`Store` instantiation is not currently supported");
690-
}
691-
}
692-
typecheck(store, module, &items, |cx, ty, item| {
693-
cx.definition(ty, item)
694-
})?;
685+
pub(crate) unsafe fn new(module: &Module, items: Vec<Definition>) -> Result<InstancePre<T>> {
686+
typecheck(module, &items, |cx, ty, item| cx.definition(ty, &item.ty()))?;
695687

696688
let host_funcs = items
697689
.iter()
@@ -813,7 +805,6 @@ fn pre_instantiate_raw(
813805
}
814806

815807
fn typecheck<I>(
816-
store: &mut StoreOpaque,
817808
module: &Module,
818809
imports: &[I],
819810
check: impl Fn(&matching::MatchCx<'_>, &EntityType, &I) -> Result<()>,
@@ -826,8 +817,7 @@ fn typecheck<I>(
826817
let cx = matching::MatchCx {
827818
signatures: module.signatures(),
828819
types: module.types(),
829-
store: store,
830-
engine: store.engine(),
820+
engine: module.engine(),
831821
};
832822
for ((name, field, expected_ty), actual) in env_module.imports().zip(imports) {
833823
check(&cx, &expected_ty, actual)

0 commit comments

Comments
 (0)