Skip to content

Commit

Permalink
Add support for initializing/getting/setting funcrefs in GC structs…
Browse files Browse the repository at this point in the history
… from host APIs (#9454)

* Add support for initializing/getting/setting `funcref`s in GC structs from host APIs

We implemented support for `funcref`s in both Wasm and host APIs for arrays, but
somehow only implemented support for `funcref`s in structs for compiled Wasm
code, and mistakenly forgot about them for structs and host APIs (and things
that use host APIs, such as const expressions).

* fix clippy
  • Loading branch information
fitzgen authored Oct 11, 2024
1 parent ad6030f commit 9529243
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 10 deletions.
7 changes: 7 additions & 0 deletions crates/wasmtime/src/runtime/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,13 @@ impl From<ValType> for StorageType {
}
}

impl From<RefType> for StorageType {
#[inline]
fn from(r: RefType) -> Self {
StorageType::ValType(r.into())
}
}

impl StorageType {
/// Is this an `i8`?
#[inline]
Expand Down
40 changes: 31 additions & 9 deletions crates/wasmtime/src/runtime/vm/gc/enabled/structref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use crate::{
prelude::*,
runtime::vm::{GcHeap, GcStore, VMGcRef},
store::AutoAssertNoGc,
AnyRef, ExternRef, HeapType, RootedGcRefImpl, StorageType, Val, ValType,
vm::{FuncRefTableId, SendSyncPtr},
AnyRef, ExternRef, Func, HeapType, RootedGcRefImpl, StorageType, Val, ValType,
};
use core::fmt;
use wasmtime_environ::{GcStructLayout, VMGcKind};
Expand Down Expand Up @@ -157,7 +158,20 @@ impl VMStructRef {
let raw = data.read_u32(offset);
Val::AnyRef(AnyRef::_from_raw(store, raw))
}
HeapType::Func => todo!("funcrefs inside gc objects not yet implemented"),
HeapType::Func => {
let func_ref_id = data.read_u32(offset);
let func_ref_id = FuncRefTableId::from_raw(func_ref_id);
let func_ref = store
.unwrap_gc_store()
.func_ref_table
.get_untyped(func_ref_id);
Val::FuncRef(unsafe {
Func::from_vm_func_ref(
store,
func_ref.map_or(core::ptr::null_mut(), |f| f.as_ptr()),
)
})
}
otherwise => unreachable!("not a top type: {otherwise:?}"),
},
}
Expand Down Expand Up @@ -230,7 +244,14 @@ impl VMStructRef {
data.write_u32(offset, gc_ref.map_or(0, |r| r.as_raw_u32()));
}

Val::FuncRef(_) => todo!("funcrefs inside gc objects not yet implemented"),
Val::FuncRef(f) => {
let f = f.map(|f| SendSyncPtr::new(f.vm_func_ref(store)));
let id = unsafe { store.gc_store_mut()?.func_ref_table.intern(f) };
store
.gc_store_mut()?
.gc_object_data(self.as_gc_ref())
.write_u32(offset, id.into_raw());
}
}
Ok(())
}
Expand Down Expand Up @@ -323,12 +344,13 @@ impl VMStructRef {
.write_u32(offset, x);
}

Val::FuncRef(_) => {
// TODO: we can't trust the GC heap, which means we can't read
// native VMFuncRef pointers out of it and trust them. That
// means we need to do the same side table kind of thing we do
// with `externref` host data here. This isn't implemented yet.
todo!("funcrefs in GC objects")
Val::FuncRef(f) => {
let f = f.map(|f| SendSyncPtr::new(f.vm_func_ref(store)));
let id = unsafe { store.gc_store_mut()?.func_ref_table.intern(f) };
store
.gc_store_mut()?
.gc_object_data(self.as_gc_ref())
.write_u32(offset, id.into_raw());
}
}
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion crates/wasmtime/src/runtime/vm/libcalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ unsafe fn intern_func_ref_for_gc_heap(instance: &mut Instance, func_ref: *mut u8
let func_ref = func_ref.cast::<VMFuncRef>();
let func_ref = NonNull::new(func_ref).map(SendSyncPtr::new);

let func_ref_id = store.unwrap_gc_store_mut().func_ref_table.intern(func_ref);
let func_ref_id = store.gc_store_mut()?.func_ref_table.intern(func_ref);
Ok(func_ref_id.into_raw())
}

Expand Down
30 changes: 30 additions & 0 deletions tests/all/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,3 +707,33 @@ fn instantiate_with_struct_global() -> Result<()> {

Ok(())
}

#[test]
fn can_put_funcrefs_in_structs() -> Result<()> {
let mut store = gc_store()?;

let struct_ty = StructType::new(
store.engine(),
[FieldType::new(Mutability::Var, RefType::FUNCREF.into())],
)?;

let f0 = Func::wrap(&mut store, |_caller: Caller<()>| -> u32 { 0x1234 });
let f1 = Func::wrap(&mut store, |_caller: Caller<()>| -> u32 { 0x5678 });

let pre = StructRefPre::new(&mut store, struct_ty.clone());
let s = StructRef::new(&mut store, &pre, &[f0.into()])?;

let f = s.field(&mut store, 0)?;
let f = f.unwrap_funcref().unwrap();
let f = f.typed::<(), u32>(&store)?;
assert_eq!(f.call(&mut store, ())?, 0x1234);

s.set_field(&mut store, 0, f1.into())?;

let f = s.field(&mut store, 0)?;
let f = f.unwrap_funcref().unwrap();
let f = f.typed::<(), u32>(&store)?;
assert_eq!(f.call(&mut store, ())?, 0x5678);

Ok(())
}

0 comments on commit 9529243

Please sign in to comment.