Build the Cranelift IR for moving the memory-allocated [DataValue]s to their correct location (e.g. register, stack) prior to calling a [CompiledFunction]. The [Function] returned by [make_trampoline] is compiled to a [Trampoline]. Note that this uses the [TargetIsa]'s default calling convention so we must also check that the [CompiledFunction] has the same calling convention (see [TestFileCompile
(name: UserFuncName, signature: &ir::Signature, isa: &dyn TargetIsa)
| 543 | /// calling convention so we must also check that the [CompiledFunction] has the same calling |
| 544 | /// convention (see [TestFileCompiler::compile]). |
| 545 | fn make_trampoline(name: UserFuncName, signature: &ir::Signature, isa: &dyn TargetIsa) -> Function { |
| 546 | // Create the trampoline signature: (callee_address: pointer, values_vec: pointer) -> () |
| 547 | let pointer_type = isa.pointer_type(); |
| 548 | let mut wrapper_sig = ir::Signature::new(isa.frontend_config().default_call_conv); |
| 549 | wrapper_sig.params.push(ir::AbiParam::new(pointer_type)); // Add the `callee_address` parameter. |
| 550 | wrapper_sig.params.push(ir::AbiParam::new(pointer_type)); // Add the `values_vec` parameter. |
| 551 | |
| 552 | let mut func = ir::Function::with_name_signature(name, wrapper_sig); |
| 553 | |
| 554 | // The trampoline has a single block filled with loads, one call to callee_address, and some loads. |
| 555 | let mut builder_context = FunctionBuilderContext::new(); |
| 556 | let mut builder = FunctionBuilder::new(&mut func, &mut builder_context); |
| 557 | let block0 = builder.create_block(); |
| 558 | builder.append_block_params_for_function_params(block0); |
| 559 | builder.switch_to_block(block0); |
| 560 | builder.seal_block(block0); |
| 561 | |
| 562 | // Extract the incoming SSA values. |
| 563 | let (callee_value, values_vec_ptr_val) = { |
| 564 | let params = builder.func.dfg.block_params(block0); |
| 565 | (params[0], params[1]) |
| 566 | }; |
| 567 | |
| 568 | // Load the argument values out of `values_vec`. |
| 569 | let callee_args = signature |
| 570 | .params |
| 571 | .iter() |
| 572 | .enumerate() |
| 573 | .map(|(i, param)| { |
| 574 | // We always store vector types in little-endian byte order as DataValue. |
| 575 | let mut flags = ir::MemFlagsData::trusted(); |
| 576 | if param.value_type.is_vector() { |
| 577 | flags.set_endianness(ir::Endianness::Little); |
| 578 | } |
| 579 | |
| 580 | // Load the value. |
| 581 | builder.ins().load( |
| 582 | param.value_type, |
| 583 | flags, |
| 584 | values_vec_ptr_val, |
| 585 | (i * UnboxedValues::SLOT_SIZE) as i32, |
| 586 | ) |
| 587 | }) |
| 588 | .collect::<Vec<_>>(); |
| 589 | |
| 590 | // Call the passed function. |
| 591 | let new_sig = builder.import_signature(signature.clone()); |
| 592 | let call = builder |
| 593 | .ins() |
| 594 | .call_indirect(new_sig, callee_value, &callee_args); |
| 595 | |
| 596 | // Store the return values into `values_vec`. |
| 597 | let results = builder.func.dfg.inst_results(call).to_vec(); |
| 598 | for ((i, value), param) in results.iter().enumerate().zip(&signature.returns) { |
| 599 | // We always store vector types in little-endian byte order as DataValue. |
| 600 | let mut flags = ir::MemFlagsData::trusted(); |
| 601 | if param.value_type.is_vector() { |
| 602 | flags.set_endianness(ir::Endianness::Little); |