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

@@ -104,7 +104,14 @@ pub(super) fn run(
// initial frame. When the inliner finishes it will return the exports of
// the root frame which are then used for recording the exports of the
// component.
let mut frames = vec![InlinerFrame::new(result, ComponentClosure::default(), args)];
let index = RuntimeComponentInstanceIndex::from_u32(0);
inliner.result.num_runtime_component_instances += 1;
let mut frames = vec![InlinerFrame::new(
index,
result,
ComponentClosure::default(),
args,
)];
let exports = inliner.run(&mut frames)?;
assert!(frames.is_empty());
@@ -195,6 +202,8 @@ struct Inliner<'a> {
/// inliner frames are stored on the heap to avoid recursion based on user
/// input.
struct InlinerFrame<'a> {
instance: RuntimeComponentInstanceIndex,
/// The remaining initializers to process when instantiating this component.
initializers: std::slice::Iter<'a, LocalInitializer<'a>>,
@@ -312,7 +321,7 @@ enum ComponentFuncDef<'a> {
/// A core wasm function was lifted into a component function.
Lifted {
ty: TypeFuncIndex,
func: CoreExport<FuncIndex>,
func: CoreDef,
options: CanonicalOptions,
},
}
@@ -509,19 +518,7 @@ impl<'a> Inliner<'a> {
let options = self.canonical_options(frame, options);
frame.component_funcs.push(ComponentFuncDef::Lifted {
ty: *ty,
func: match frame.funcs[*func].clone() {
CoreDef::Export(e) => e.map_index(|i| match i {
EntityIndex::Function(i) => i,
_ => unreachable!("not possible in valid components"),
}),
// TODO: lifting a lowered function only happens within
// one component so this runs afoul of "someone needs to
// really closely interpret the may_{enter,leave} flags"
// in the component model spec. That has not currently
// been done so this is left to panic.
CoreDef::Lowered(_) => unimplemented!("lifting a lowered function"),
},
func: frame.funcs[*func].clone(),
options,
});
}
@@ -618,7 +615,12 @@ impl<'a> Inliner<'a> {
// stack.
ComponentInstantiate(component, args) => {
let component: &ComponentDef<'a> = &frame.components[*component];
let index = RuntimeComponentInstanceIndex::from_u32(
self.result.num_runtime_component_instances,
);
self.result.num_runtime_component_instances += 1;
let frame = InlinerFrame::new(
index,
&self.nested_components[component.index],
component.closure.clone(),
args.iter()
@@ -872,6 +874,7 @@ impl<'a> Inliner<'a> {
})
});
CanonicalOptions {
instance: frame.instance,
string_encoding: options.string_encoding,
memory,
realloc,
@@ -882,6 +885,7 @@ impl<'a> Inliner<'a> {
impl<'a> InlinerFrame<'a> {
fn new(
instance: RuntimeComponentInstanceIndex,
translation: &'a Translation<'a>,
closure: ComponentClosure<'a>,
args: HashMap<&'a str, ComponentItemDef<'a>>,
@@ -891,6 +895,7 @@ impl<'a> InlinerFrame<'a> {
// all the maps below. Given that doing such would be wordy and compile
// time is otherwise not super crucial it's not done at this time.
InlinerFrame {
instance,
translation,
closure,
args,