Skip to content

Commit 3f5d7b1

Browse files
committed
add component-model-async/fused.wast test
This is another piece of bytecodealliance#9582 which I'm splitting out to make review easier. This test exercises fused adapter generation for various flavors of intercomponent async->async, async->sync, and sync->async calls. The remaining changes fill in some TODOs to make the test pass. Signed-off-by: Joel Dice <[email protected]>
1 parent b131a1a commit 3f5d7b1

File tree

9 files changed

+1403
-103
lines changed

9 files changed

+1403
-103
lines changed

crates/cranelift/src/compiler/component.rs

+176-24
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::{compiler::Compiler, TRAP_ALWAYS, TRAP_CANNOT_ENTER, TRAP_INTERNAL_ASSERT};
44
use anyhow::Result;
55
use cranelift_codegen::ir::condcodes::IntCC;
6-
use cranelift_codegen::ir::{self, InstBuilder, MemFlags};
6+
use cranelift_codegen::ir::{self, InstBuilder, MemFlags, Value};
77
use cranelift_codegen::isa::{CallConv, TargetIsa};
88
use cranelift_frontend::FunctionBuilder;
99
use std::any::Any;
@@ -98,7 +98,7 @@ impl<'a> TrampolineCompiler<'a> {
9898
_ = instance;
9999
todo!()
100100
}
101-
Trampoline::TaskReturn => todo!(),
101+
Trampoline::TaskReturn => self.translate_task_return_call(),
102102
Trampoline::TaskWait {
103103
instance,
104104
async_,
@@ -196,12 +196,12 @@ impl<'a> TrampolineCompiler<'a> {
196196
Trampoline::ResourceDrop(ty) => self.translate_resource_drop(*ty),
197197
Trampoline::ResourceTransferOwn => {
198198
self.translate_resource_libcall(host::resource_transfer_own, |me, rets| {
199-
rets[0] = me.raise_if_resource_trapped(rets[0]);
199+
rets[0] = me.raise_if_i32_trapped(rets[0]);
200200
})
201201
}
202202
Trampoline::ResourceTransferBorrow => {
203203
self.translate_resource_libcall(host::resource_transfer_borrow, |me, rets| {
204-
rets[0] = me.raise_if_resource_trapped(rets[0]);
204+
rets[0] = me.raise_if_i32_trapped(rets[0]);
205205
})
206206
}
207207
Trampoline::ResourceEnterCall => {
@@ -212,14 +212,17 @@ impl<'a> TrampolineCompiler<'a> {
212212
me.raise_if_host_trapped(rets.pop().unwrap());
213213
})
214214
}
215-
Trampoline::AsyncEnterCall => todo!(),
215+
Trampoline::AsyncEnterCall => {
216+
self.translate_async_enter_or_exit(host::async_enter, None, ir::types::I8)
217+
}
216218
Trampoline::AsyncExitCall {
217219
callback,
218220
post_return,
219-
} => {
220-
_ = (callback, post_return);
221-
todo!()
222-
}
221+
} => self.translate_async_enter_or_exit(
222+
host::async_exit,
223+
Some((*callback, *post_return)),
224+
ir::types::I64,
225+
),
223226
Trampoline::FutureTransfer => {
224227
_ = host::future_transfer;
225228
todo!()
@@ -235,25 +238,18 @@ impl<'a> TrampolineCompiler<'a> {
235238
}
236239
}
237240

238-
fn translate_lower_import(
239-
&mut self,
240-
index: LoweredIndex,
241-
options: &CanonicalOptions,
242-
lower_ty: TypeFuncIndex,
243-
) {
241+
fn store_wasm_arguments(&mut self, args: &[Value]) -> (Value, Value) {
244242
let pointer_type = self.isa.pointer_type();
245-
let args = self.builder.func.dfg.block_params(self.block0).to_vec();
246-
let vmctx = args[0];
247-
let wasm_func_ty = self.types[self.signature].unwrap_func();
243+
let wasm_func_ty = &self.types[self.signature].unwrap_func();
248244

249245
// Start off by spilling all the wasm arguments into a stack slot to be
250246
// passed to the host function.
251-
let (values_vec_ptr, values_vec_len) = match self.abi {
247+
match self.abi {
252248
Abi::Wasm => {
253249
let (ptr, len) = self.compiler.allocate_stack_array_and_spill_args(
254250
wasm_func_ty,
255251
&mut self.builder,
256-
&args[2..],
252+
args,
257253
);
258254
let len = self.builder.ins().iconst(pointer_type, i64::from(len));
259255
(ptr, len)
@@ -262,7 +258,163 @@ impl<'a> TrampolineCompiler<'a> {
262258
let params = self.builder.func.dfg.block_params(self.block0);
263259
(params[2], params[3])
264260
}
265-
};
261+
}
262+
}
263+
264+
fn translate_task_return_call(&mut self) {
265+
match self.abi {
266+
Abi::Wasm => {}
267+
268+
// These trampolines can only actually be called by Wasm, so
269+
// let's assert that here.
270+
Abi::Array => {
271+
self.builder.ins().trap(TRAP_INTERNAL_ASSERT);
272+
return;
273+
}
274+
}
275+
276+
let args = self.builder.func.dfg.block_params(self.block0).to_vec();
277+
let vmctx = args[0];
278+
279+
let (values_vec_ptr, values_vec_len) = self.store_wasm_arguments(&args[2..]);
280+
281+
let (host_sig, index) = host::task_return(self.isa, &mut self.builder.func);
282+
let host_fn = self.load_libcall(vmctx, index);
283+
284+
let params = self.types[self.signature]
285+
.unwrap_func()
286+
.params()
287+
.iter()
288+
.map(|&v| {
289+
Some(match v {
290+
WasmValType::I32 => FlatType::I32,
291+
WasmValType::I64 => FlatType::I64,
292+
WasmValType::F32 => FlatType::F32,
293+
WasmValType::F64 => FlatType::F64,
294+
_ => return None,
295+
})
296+
})
297+
.collect::<Option<_>>();
298+
299+
let ty = self.builder.ins().iconst(
300+
ir::types::I32,
301+
i64::from(
302+
params
303+
.and_then(|params| {
304+
self.types
305+
.get_task_return_type(&TypeTaskReturn { params })
306+
.map(|v| v.as_u32())
307+
})
308+
.unwrap_or(u32::MAX),
309+
),
310+
);
311+
312+
let call = self.compiler.call_indirect_host(
313+
&mut self.builder,
314+
index,
315+
host_sig,
316+
host_fn,
317+
&[vmctx, ty, values_vec_ptr, values_vec_len],
318+
);
319+
let succeeded = self.builder.func.dfg.inst_results(call)[0];
320+
self.raise_if_host_trapped(succeeded);
321+
self.builder.ins().return_(&[]);
322+
}
323+
324+
fn translate_async_enter_or_exit(
325+
&mut self,
326+
get_libcall: fn(
327+
&dyn TargetIsa,
328+
&mut ir::Function,
329+
) -> (ir::SigRef, ComponentBuiltinFunctionIndex),
330+
callback_and_post_return: Option<(
331+
Option<RuntimeCallbackIndex>,
332+
Option<RuntimePostReturnIndex>,
333+
)>,
334+
result: ir::types::Type,
335+
) {
336+
match self.abi {
337+
Abi::Wasm => {}
338+
339+
// These trampolines can only actually be called by Wasm, so
340+
// let's assert that here.
341+
Abi::Array => {
342+
self.builder.ins().trap(TRAP_INTERNAL_ASSERT);
343+
return;
344+
}
345+
}
346+
347+
let args = self.builder.func.dfg.block_params(self.block0).to_vec();
348+
let vmctx = args[0];
349+
350+
let (host_sig, index) = get_libcall(self.isa, &mut self.builder.func);
351+
let host_fn = self.load_libcall(vmctx, index);
352+
353+
let mut callee_args = vec![vmctx];
354+
355+
if let Some((callback, post_return)) = callback_and_post_return {
356+
let pointer_type = self.isa.pointer_type();
357+
358+
// callback: *mut VMFuncRef
359+
if let Some(callback) = callback {
360+
callee_args.push(self.builder.ins().load(
361+
pointer_type,
362+
MemFlags::trusted(),
363+
vmctx,
364+
i32::try_from(self.offsets.runtime_callback(callback)).unwrap(),
365+
));
366+
} else {
367+
callee_args.push(self.builder.ins().iconst(pointer_type, 0));
368+
}
369+
370+
// post_return: *mut VMFuncRef
371+
if let Some(post_return) = post_return {
372+
callee_args.push(self.builder.ins().load(
373+
pointer_type,
374+
MemFlags::trusted(),
375+
vmctx,
376+
i32::try_from(self.offsets.runtime_post_return(post_return)).unwrap(),
377+
));
378+
} else {
379+
callee_args.push(self.builder.ins().iconst(pointer_type, 0));
380+
}
381+
}
382+
383+
// remaining parameters
384+
callee_args.extend(args[2..].iter().copied());
385+
386+
let call = self.compiler.call_indirect_host(
387+
&mut self.builder,
388+
index,
389+
host_sig,
390+
host_fn,
391+
&callee_args,
392+
);
393+
394+
if result == ir::types::I64 {
395+
let result = self.builder.func.dfg.inst_results(call)[0];
396+
let result = self.raise_if_i32_trapped(result);
397+
self.abi_store_results(&[result]);
398+
} else {
399+
assert!(result == ir::types::I8);
400+
let succeeded = self.builder.func.dfg.inst_results(call)[0];
401+
self.raise_if_host_trapped(succeeded);
402+
self.builder.ins().return_(&[]);
403+
}
404+
}
405+
406+
fn translate_lower_import(
407+
&mut self,
408+
index: LoweredIndex,
409+
options: &CanonicalOptions,
410+
lower_ty: TypeFuncIndex,
411+
) {
412+
let pointer_type = self.isa.pointer_type();
413+
let args = self.builder.func.dfg.block_params(self.block0).to_vec();
414+
let vmctx = args[0];
415+
let wasm_func_ty = self.types[self.signature].unwrap_func();
416+
417+
let (values_vec_ptr, values_vec_len) = self.store_wasm_arguments(&args[2..]);
266418

267419
// Below this will incrementally build both the signature of the host
268420
// function we're calling as well as the list of arguments since the
@@ -457,7 +609,7 @@ impl<'a> TrampolineCompiler<'a> {
457609
);
458610
let call = self.call_libcall(vmctx, host::resource_new32, &host_args);
459611
let result = self.builder.func.dfg.inst_results(call)[0];
460-
let result = self.raise_if_resource_trapped(result);
612+
let result = self.raise_if_i32_trapped(result);
461613
self.abi_store_results(&[result]);
462614
}
463615

@@ -486,7 +638,7 @@ impl<'a> TrampolineCompiler<'a> {
486638
);
487639
let call = self.call_libcall(vmctx, host::resource_rep32, &host_args);
488640
let result = self.builder.func.dfg.inst_results(call)[0];
489-
let result = self.raise_if_resource_trapped(result);
641+
let result = self.raise_if_i32_trapped(result);
490642
self.abi_store_results(&[result]);
491643
}
492644

@@ -799,7 +951,7 @@ impl<'a> TrampolineCompiler<'a> {
799951
self.raise_if_host_trapped(succeeded);
800952
}
801953

802-
fn raise_if_resource_trapped(&mut self, ret: ir::Value) -> ir::Value {
954+
fn raise_if_i32_trapped(&mut self, ret: ir::Value) -> ir::Value {
803955
let minus_one = self.builder.ins().iconst(ir::types::I64, -1);
804956
let succeeded = self.builder.ins().icmp(IntCC::NotEqual, ret, minus_one);
805957
self.raise_if_host_trapped(succeeded);

crates/environ/src/component.rs

+4
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ macro_rules! foreach_builtin_component_function {
8383
resource_enter_call(vmctx: vmctx);
8484
resource_exit_call(vmctx: vmctx) -> bool;
8585

86+
task_return(vmctx: vmctx, ty: u32, storage: ptr_u8, storage_len: size) -> bool;
87+
async_enter(vmctx: vmctx, start: ptr_u8, return_: ptr_u8, caller_instance: u32, task_return_type: u32, params: u32, results: u32) -> bool;
88+
async_exit(vmctx: vmctx, callback: ptr_u8, post_return: ptr_u8, caller_instance: u32, callee: ptr_u8, callee_instance: u32, param_count: u32, result_count: u32, flags: u32) -> u64;
89+
8690
future_transfer(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u64;
8791
stream_transfer(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u64;
8892
error_context_transfer(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u64;

0 commit comments

Comments
 (0)