Implement lowered-then-lifted functions (#4327)

* Implement lowered-then-lifted functions

This commit is a few features bundled into one, culminating in the
implementation of lowered-then-lifted functions for the component model.
It's probably not going to be used all that often but this is possible
within a valid component so Wasmtime needs to do something relatively
reasonable. The main things implemented in this commit are:

* Component instances are now assigned a `RuntimeComponentInstanceIndex`
  to differentiate each one. This will be used in the future to detect
  fusion (one instance lowering a function from another instance). For
  now it's used to allocate separate `VMComponentFlags` for each
  internal component instance.

* The `CoreExport<FuncIndex>` of lowered functions was changed to a
  `CoreDef` since technically a lowered function can use another lowered
  function as the callee. This ended up being not too difficult to plumb
  through as everything else was already in place.

* A need arose to compile host-to-wasm trampolines which weren't already
  present. Currently wasm in a component is always entered through a
  host-to-wasm trampoline but core wasm modules are the source of all
  the trampolines. In the case of a lowered-then-lifted function there
  may not actually be any core wasm modules, so component objects now
  contain necessary trampolines not otherwise provided by the core wasm
  objects. This feature required splitting a new function into the
  `Compiler` trait for creating a host-to-wasm trampoline. After doing
  this core wasm compilation was also updated to leverage this which
  further enabled compiling trampolines in parallel as opposed to the
  previous synchronous compilation.

* Review comments
This commit is contained in:
Alex Crichton
2022-06-28 13:50:08 -05:00
committed by GitHub
parent df1502531d
commit c1b3962f7b
17 changed files with 400 additions and 107 deletions

View File

@@ -2152,5 +2152,120 @@ fn raw_slice_of_various_types() -> Result<()> {
i64::to_le(0x0f_0e_0d_0c_0b_0a_09_08),
]
);
Ok(())
}
#[test]
fn lower_then_lift() -> Result<()> {
// First test simple integers when the import/export ABI happen to line up
let component = r#"
(component $c
(import "f" (func $f (result u32)))
(core func $f_lower
(canon lower (func $f))
)
(func $f2 (result s32)
(canon lift (core func $f_lower))
)
(export "f" (func $f2))
)
"#;
let engine = super::engine();
let component = Component::new(&engine, component)?;
let mut store = Store::new(&engine, ());
let mut linker = Linker::new(&engine);
linker.root().func_wrap("f", || Ok(2u32))?;
let instance = linker.instantiate(&mut store, &component)?;
let f = instance.get_typed_func::<(), i32, _>(&mut store, "f")?;
assert_eq!(f.call(&mut store, ())?, 2);
// First test strings when the import/export ABI happen to line up
let component = format!(
r#"
(component $c
(import "s" (func $f (param string)))
(core module $libc
(memory (export "memory") 1)
{REALLOC_AND_FREE}
)
(core instance $libc (instantiate $libc))
(core func $f_lower
(canon lower (func $f) (memory $libc "memory"))
)
(func $f2 (param string)
(canon lift (core func $f_lower)
(memory $libc "memory")
(realloc (func $libc "realloc"))
)
)
(export "f" (func $f2))
)
"#
);
let component = Component::new(&engine, component)?;
let mut store = Store::new(&engine, ());
linker
.root()
.func_wrap("s", |store: StoreContextMut<'_, ()>, x: WasmStr| {
assert_eq!(x.to_str(&store)?, "hello");
Ok(())
})?;
let instance = linker.instantiate(&mut store, &component)?;
let f = instance.get_typed_func::<(&str,), (), _>(&mut store, "f")?;
f.call(&mut store, ("hello",))?;
// Next test "type punning" where return values are reinterpreted just
// because the return ABI happens to line up.
let component = format!(
r#"
(component $c
(import "s2" (func $f (param string) (result u32)))
(core module $libc
(memory (export "memory") 1)
{REALLOC_AND_FREE}
)
(core instance $libc (instantiate $libc))
(core func $f_lower
(canon lower (func $f) (memory $libc "memory"))
)
(func $f2 (param string) (result string)
(canon lift (core func $f_lower)
(memory $libc "memory")
(realloc (func $libc "realloc"))
)
)
(export "f" (func $f2))
)
"#
);
let component = Component::new(&engine, component)?;
let mut store = Store::new(&engine, ());
linker
.root()
.func_wrap("s2", |store: StoreContextMut<'_, ()>, x: WasmStr| {
assert_eq!(x.to_str(&store)?, "hello");
Ok(u32::MAX)
})?;
let instance = linker.instantiate(&mut store, &component)?;
let f = instance.get_typed_func::<(&str,), WasmStr, _>(&mut store, "f")?;
let err = f.call(&mut store, ("hello",)).err().unwrap();
assert!(
err.to_string().contains("return pointer not aligned"),
"{}",
err
);
Ok(())
}

View File

@@ -70,3 +70,16 @@
)
)
)
;; lower something then immediately lift it
(component $c
(import "host-return-two" (func $f (result u32)))
(core func $f_lower
(canon lower (func $f))
)
(func $f2 (result s32)
(canon lift (core func $f_lower))
)
(export "f" (func $f2))
)