Skip to content

Commit 472c11e

Browse files
alexcrichtonafonso360
authored andcommitted
Implement lowered-then-lifted functions (bytecodealliance#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
1 parent 7301580 commit 472c11e

File tree

17 files changed

+400
-107
lines changed

17 files changed

+400
-107
lines changed

crates/cranelift/src/compiler.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -288,30 +288,37 @@ impl wasmtime_environ::Compiler for Compiler {
288288
}))
289289
}
290290

291+
fn compile_host_to_wasm_trampoline(
292+
&self,
293+
ty: &WasmFuncType,
294+
) -> Result<Box<dyn Any + Send>, CompileError> {
295+
self.host_to_wasm_trampoline(ty)
296+
.map(|x| Box::new(x) as Box<_>)
297+
}
298+
291299
fn emit_obj(
292300
&self,
293301
translation: &ModuleTranslation,
294-
types: &ModuleTypes,
295302
funcs: PrimaryMap<DefinedFuncIndex, Box<dyn Any + Send>>,
303+
compiled_trampolines: Vec<Box<dyn Any + Send>>,
296304
tunables: &Tunables,
297305
obj: &mut Object<'static>,
298306
) -> Result<(PrimaryMap<DefinedFuncIndex, FunctionInfo>, Vec<Trampoline>)> {
299307
let funcs: CompiledFunctions = funcs
300308
.into_iter()
301309
.map(|(_i, f)| *f.downcast().unwrap())
302310
.collect();
311+
let compiled_trampolines: Vec<CompiledFunction> = compiled_trampolines
312+
.into_iter()
313+
.map(|f| *f.downcast().unwrap())
314+
.collect();
303315

304316
let mut builder = ModuleTextBuilder::new(obj, &translation.module, &*self.isa);
305317
if self.linkopts.force_jump_veneers {
306318
builder.force_veneers();
307319
}
308320
let mut addrs = AddressMapSection::default();
309321
let mut traps = TrapEncodingBuilder::default();
310-
let compiled_trampolines = translation
311-
.exported_signatures
312-
.iter()
313-
.map(|i| self.host_to_wasm_trampoline(&types[*i]))
314-
.collect::<Result<Vec<_>, _>>()?;
315322

316323
let mut func_starts = Vec::with_capacity(funcs.len());
317324
for (i, func) in funcs.iter() {
@@ -325,6 +332,10 @@ impl wasmtime_environ::Compiler for Compiler {
325332
}
326333

327334
// Build trampolines for every signature that can be used by this module.
335+
assert_eq!(
336+
translation.exported_signatures.len(),
337+
compiled_trampolines.len()
338+
);
328339
let mut trampolines = Vec::with_capacity(translation.exported_signatures.len());
329340
for (i, func) in translation
330341
.exported_signatures

crates/cranelift/src/compiler/component.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use object::write::Object;
1010
use std::any::Any;
1111
use wasmtime_environ::component::{
1212
CanonicalOptions, Component, ComponentCompiler, ComponentTypes, LowerImport, LoweredIndex,
13-
TrampolineInfo, VMComponentOffsets,
13+
LoweringInfo, VMComponentOffsets,
1414
};
15-
use wasmtime_environ::PrimaryMap;
15+
use wasmtime_environ::{PrimaryMap, SignatureIndex, Trampoline};
1616

1717
impl ComponentCompiler for Compiler {
1818
fn compile_lowered_trampoline(
@@ -52,6 +52,7 @@ impl ComponentCompiler for Compiler {
5252
let mut host_sig = ir::Signature::new(crate::wasmtime_call_conv(isa));
5353

5454
let CanonicalOptions {
55+
instance,
5556
memory,
5657
realloc,
5758
post_return,
@@ -71,6 +72,14 @@ impl ComponentCompiler for Compiler {
7172
i32::try_from(offsets.lowering_data(lowering.index)).unwrap(),
7273
));
7374

75+
// flags: *mut VMComponentFlags
76+
host_sig.params.push(ir::AbiParam::new(pointer_type));
77+
callee_args.push(
78+
builder
79+
.ins()
80+
.iadd_imm(vmctx, i64::from(offsets.flags(instance))),
81+
);
82+
7483
// memory: *mut VMMemoryDefinition
7584
host_sig.params.push(ir::AbiParam::new(pointer_type));
7685
callee_args.push(match memory {
@@ -145,32 +154,42 @@ impl ComponentCompiler for Compiler {
145154

146155
fn emit_obj(
147156
&self,
148-
trampolines: PrimaryMap<LoweredIndex, Box<dyn Any + Send>>,
157+
lowerings: PrimaryMap<LoweredIndex, Box<dyn Any + Send>>,
158+
trampolines: Vec<(SignatureIndex, Box<dyn Any + Send>)>,
149159
obj: &mut Object<'static>,
150-
) -> Result<PrimaryMap<LoweredIndex, TrampolineInfo>> {
151-
let trampolines: PrimaryMap<LoweredIndex, CompiledFunction> = trampolines
160+
) -> Result<(PrimaryMap<LoweredIndex, LoweringInfo>, Vec<Trampoline>)> {
161+
let lowerings: PrimaryMap<LoweredIndex, CompiledFunction> = lowerings
152162
.into_iter()
153163
.map(|(_, f)| *f.downcast().unwrap())
154164
.collect();
165+
let trampolines: Vec<(SignatureIndex, CompiledFunction)> = trampolines
166+
.into_iter()
167+
.map(|(i, f)| (i, *f.downcast().unwrap()))
168+
.collect();
169+
155170
let module = Default::default();
156171
let mut text = ModuleTextBuilder::new(obj, &module, &*self.isa);
157172
let mut ret = PrimaryMap::new();
158-
for (idx, trampoline) in trampolines.iter() {
173+
for (idx, lowering) in lowerings.iter() {
159174
let (_symbol, range) = text.append_func(
160175
false,
161-
format!("_wasm_component_host_trampoline{}", idx.as_u32()).into_bytes(),
162-
&trampoline,
176+
format!("_wasm_component_lowering_trampoline{}", idx.as_u32()).into_bytes(),
177+
&lowering,
163178
);
164179

165-
let i = ret.push(TrampolineInfo {
180+
let i = ret.push(LoweringInfo {
166181
start: u32::try_from(range.start).unwrap(),
167182
length: u32::try_from(range.end - range.start).unwrap(),
168183
});
169184
assert_eq!(i, idx);
170185
}
186+
let ret_trampolines = trampolines
187+
.iter()
188+
.map(|(i, func)| text.trampoline(*i, func))
189+
.collect();
171190

172191
text.finish()?;
173192

174-
Ok(ret)
193+
Ok((ret, ret_trampolines))
175194
}
176195
}

crates/environ/src/compilation.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,21 @@ pub trait Compiler: Send + Sync {
148148
types: &ModuleTypes,
149149
) -> Result<Box<dyn Any + Send>, CompileError>;
150150

151+
/// Creates a function of type `VMTrampoline` which will then call the
152+
/// function pointer argument which has the `ty` type provided.
153+
fn compile_host_to_wasm_trampoline(
154+
&self,
155+
ty: &WasmFuncType,
156+
) -> Result<Box<dyn Any + Send>, CompileError>;
157+
151158
/// Collects the results of compilation into an in-memory object.
152159
///
153160
/// This function will receive the same `Box<dyn Ayn>` produced as part of
154161
/// `compile_function`, as well as the general compilation environment with
155-
/// the translation/types. This method is expected to populate information
156-
/// in the object file such as:
162+
/// the translation. THe `trampolines` argument is generated by
163+
/// `compile_host_to_wasm_trampoline` for each of
164+
/// `module.exported_signatures`. This method is expected to populate
165+
/// information in the object file such as:
157166
///
158167
/// * Compiled code in a `.text` section
159168
/// * Unwind information in Wasmtime-specific sections
@@ -163,11 +172,14 @@ pub trait Compiler: Send + Sync {
163172
///
164173
/// The final result of compilation will contain more sections inserted by
165174
/// the compiler-agnostic runtime.
175+
///
176+
/// This function returns information about the compiled functions (where
177+
/// they are in the text section) along with where trampolines are located.
166178
fn emit_obj(
167179
&self,
168180
module: &ModuleTranslation,
169-
types: &ModuleTypes,
170181
funcs: PrimaryMap<DefinedFuncIndex, Box<dyn Any + Send>>,
182+
trampolines: Vec<Box<dyn Any + Send>>,
171183
tunables: &Tunables,
172184
obj: &mut Object<'static>,
173185
) -> Result<(PrimaryMap<DefinedFuncIndex, FunctionInfo>, Vec<Trampoline>)>;

crates/environ/src/component/compiler.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::component::{Component, ComponentTypes, LowerImport, LoweredIndex};
2-
use crate::PrimaryMap;
2+
use crate::{PrimaryMap, SignatureIndex, Trampoline};
33
use anyhow::Result;
44
use object::write::Object;
55
use serde::{Deserialize, Serialize};
@@ -8,7 +8,7 @@ use std::any::Any;
88
/// Description of where a trampoline is located in the text section of a
99
/// compiled image.
1010
#[derive(Serialize, Deserialize)]
11-
pub struct TrampolineInfo {
11+
pub struct LoweringInfo {
1212
/// The byte offset from the start of the text section where this trampoline
1313
/// starts.
1414
pub start: u32,
@@ -42,8 +42,8 @@ pub trait ComponentCompiler: Send + Sync {
4242
types: &ComponentTypes,
4343
) -> Result<Box<dyn Any + Send>>;
4444

45-
/// Emits the `trampolines` specified into the in-progress ELF object
46-
/// specified by `obj`.
45+
/// Emits the `lowerings` and `trampolines` specified into the in-progress
46+
/// ELF object specified by `obj`.
4747
///
4848
/// Returns a map of trampoline information for where to find them all in
4949
/// the text section.
@@ -52,7 +52,8 @@ pub trait ComponentCompiler: Send + Sync {
5252
/// trampolines as necessary.
5353
fn emit_obj(
5454
&self,
55-
trampolines: PrimaryMap<LoweredIndex, Box<dyn Any + Send>>,
55+
lowerings: PrimaryMap<LoweredIndex, Box<dyn Any + Send>>,
56+
tramplines: Vec<(SignatureIndex, Box<dyn Any + Send>)>,
5657
obj: &mut Object<'static>,
57-
) -> Result<PrimaryMap<LoweredIndex, TrampolineInfo>>;
58+
) -> Result<(PrimaryMap<LoweredIndex, LoweringInfo>, Vec<Trampoline>)>;
5859
}

crates/environ/src/component/info.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ pub struct Component {
114114
/// when instantiating this component.
115115
pub num_runtime_instances: u32,
116116

117+
/// Same as `num_runtime_instances`, but for `RuntimeComponentInstanceIndex`
118+
/// instead.
119+
pub num_runtime_component_instances: u32,
120+
117121
/// The number of runtime memories (maximum `RuntimeMemoryIndex`) needed to
118122
/// instantiate this component.
119123
///
@@ -355,7 +359,7 @@ pub enum Export {
355359
/// The component function type of the function being created.
356360
ty: TypeFuncIndex,
357361
/// Which core WebAssembly export is being lifted.
358-
func: CoreExport<FuncIndex>,
362+
func: CoreDef,
359363
/// Any options, if present, associated with this lifting.
360364
options: CanonicalOptions,
361365
},
@@ -369,6 +373,9 @@ pub enum Export {
369373
/// Canonical ABI options associated with a lifted or lowered function.
370374
#[derive(Debug, Clone, Serialize, Deserialize)]
371375
pub struct CanonicalOptions {
376+
/// The component instance that this bundle was associated with.
377+
pub instance: RuntimeComponentInstanceIndex,
378+
372379
/// The encoding used for strings.
373380
pub string_encoding: StringEncoding,
374381

@@ -382,17 +389,6 @@ pub struct CanonicalOptions {
382389
pub post_return: Option<RuntimePostReturnIndex>,
383390
}
384391

385-
impl Default for CanonicalOptions {
386-
fn default() -> CanonicalOptions {
387-
CanonicalOptions {
388-
string_encoding: StringEncoding::Utf8,
389-
memory: None,
390-
realloc: None,
391-
post_return: None,
392-
}
393-
}
394-
}
395-
396392
/// Possible encodings of strings within the component model.
397393
//
398394
// Note that the `repr(u8)` is load-bearing here since this is used in an

crates/environ/src/component/translate/inline.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,14 @@ pub(super) fn run(
104104
// initial frame. When the inliner finishes it will return the exports of
105105
// the root frame which are then used for recording the exports of the
106106
// component.
107-
let mut frames = vec![InlinerFrame::new(result, ComponentClosure::default(), args)];
107+
let index = RuntimeComponentInstanceIndex::from_u32(0);
108+
inliner.result.num_runtime_component_instances += 1;
109+
let mut frames = vec![InlinerFrame::new(
110+
index,
111+
result,
112+
ComponentClosure::default(),
113+
args,
114+
)];
108115
let exports = inliner.run(&mut frames)?;
109116
assert!(frames.is_empty());
110117

@@ -195,6 +202,8 @@ struct Inliner<'a> {
195202
/// inliner frames are stored on the heap to avoid recursion based on user
196203
/// input.
197204
struct InlinerFrame<'a> {
205+
instance: RuntimeComponentInstanceIndex,
206+
198207
/// The remaining initializers to process when instantiating this component.
199208
initializers: std::slice::Iter<'a, LocalInitializer<'a>>,
200209

@@ -312,7 +321,7 @@ enum ComponentFuncDef<'a> {
312321
/// A core wasm function was lifted into a component function.
313322
Lifted {
314323
ty: TypeFuncIndex,
315-
func: CoreExport<FuncIndex>,
324+
func: CoreDef,
316325
options: CanonicalOptions,
317326
},
318327
}
@@ -509,19 +518,7 @@ impl<'a> Inliner<'a> {
509518
let options = self.canonical_options(frame, options);
510519
frame.component_funcs.push(ComponentFuncDef::Lifted {
511520
ty: *ty,
512-
func: match frame.funcs[*func].clone() {
513-
CoreDef::Export(e) => e.map_index(|i| match i {
514-
EntityIndex::Function(i) => i,
515-
_ => unreachable!("not possible in valid components"),
516-
}),
517-
518-
// TODO: lifting a lowered function only happens within
519-
// one component so this runs afoul of "someone needs to
520-
// really closely interpret the may_{enter,leave} flags"
521-
// in the component model spec. That has not currently
522-
// been done so this is left to panic.
523-
CoreDef::Lowered(_) => unimplemented!("lifting a lowered function"),
524-
},
521+
func: frame.funcs[*func].clone(),
525522
options,
526523
});
527524
}
@@ -618,7 +615,12 @@ impl<'a> Inliner<'a> {
618615
// stack.
619616
ComponentInstantiate(component, args) => {
620617
let component: &ComponentDef<'a> = &frame.components[*component];
618+
let index = RuntimeComponentInstanceIndex::from_u32(
619+
self.result.num_runtime_component_instances,
620+
);
621+
self.result.num_runtime_component_instances += 1;
621622
let frame = InlinerFrame::new(
623+
index,
622624
&self.nested_components[component.index],
623625
component.closure.clone(),
624626
args.iter()
@@ -872,6 +874,7 @@ impl<'a> Inliner<'a> {
872874
})
873875
});
874876
CanonicalOptions {
877+
instance: frame.instance,
875878
string_encoding: options.string_encoding,
876879
memory,
877880
realloc,
@@ -882,6 +885,7 @@ impl<'a> Inliner<'a> {
882885

883886
impl<'a> InlinerFrame<'a> {
884887
fn new(
888+
instance: RuntimeComponentInstanceIndex,
885889
translation: &'a Translation<'a>,
886890
closure: ComponentClosure<'a>,
887891
args: HashMap<&'a str, ComponentItemDef<'a>>,
@@ -891,6 +895,7 @@ impl<'a> InlinerFrame<'a> {
891895
// all the maps below. Given that doing such would be wordy and compile
892896
// time is otherwise not super crucial it's not done at this time.
893897
InlinerFrame {
898+
instance,
894899
translation,
895900
closure,
896901
args,

crates/environ/src/component/types.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ indices! {
122122
/// refer back to previously created instances for exports and such.
123123
pub struct RuntimeInstanceIndex(u32);
124124

125+
/// Same as `RuntimeInstanceIndex` but tracks component instances instead.
126+
pub struct RuntimeComponentInstanceIndex(u32);
127+
125128
/// Used to index imports into a `Component`
126129
///
127130
/// This does not correspond to anything in the binary format for the

0 commit comments

Comments
 (0)