diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 4dc192b0b..e49202e3a 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -306,16 +306,15 @@ impl<'a> DynamicFunc<'a> { { use crate::trampoline_x64::{CallContext, TrampolineBufferBuilder}; use crate::types::Value; - use std::convert::TryFrom; struct PolymorphicContext { arg_types: Vec, func: Box Vec>, } - unsafe extern "C" fn enter_host_polymorphic( + unsafe fn do_enter_host_polymorphic( ctx: *const CallContext, args: *const u64, - ) -> u64 { + ) -> Vec { let ctx = &*(ctx as *const PolymorphicContext); let vmctx = &mut *(*args.offset(0) as *mut vm::Ctx); let args: Vec = ctx @@ -335,13 +334,40 @@ impl<'a> DynamicFunc<'a> { } }) .collect(); - let rets = (ctx.func)(vmctx, &args); + (ctx.func)(vmctx, &args) + } + unsafe extern "C" fn enter_host_polymorphic_i( + ctx: *const CallContext, + args: *const u64, + ) -> u64 { + let rets = do_enter_host_polymorphic(ctx, args); if rets.len() == 0 { 0 } else if rets.len() == 1 { - u64::try_from(rets[0].to_u128()).expect( - "128-bit return value from polymorphic host functions is not yet supported", - ) + match rets[0] { + Value::I32(x) => x as u64, + Value::I64(x) => x as u64, + _ => panic!("enter_host_polymorphic_i: invalid return type"), + } + } else { + panic!( + "multiple return values from polymorphic host functions is not yet supported" + ); + } + } + unsafe extern "C" fn enter_host_polymorphic_f( + ctx: *const CallContext, + args: *const u64, + ) -> f64 { + let rets = do_enter_host_polymorphic(ctx, args); + if rets.len() == 0 { + 0.0 + } else if rets.len() == 1 { + match rets[0] { + Value::F32(x) => f64::from_bits(x.to_bits() as u64), + Value::F64(x) => x, + _ => panic!("enter_host_polymorphic_f: invalid return type"), + } } else { panic!( "multiple return values from polymorphic host functions is not yet supported" @@ -364,11 +390,23 @@ impl<'a> DynamicFunc<'a> { let mut native_param_types = vec![Type::I32]; // vm::Ctx is the first parameter. native_param_types.extend_from_slice(signature.params()); - builder.add_callinfo_trampoline( - enter_host_polymorphic, - ctx as *const _, - &native_param_types, - ); + match signature.returns() { + [x] if *x == Type::F32 || *x == Type::F64 => { + builder.add_callinfo_trampoline( + unsafe { std::mem::transmute(enter_host_polymorphic_f as usize) }, + ctx as *const _, + &native_param_types, + ); + } + _ => { + builder.add_callinfo_trampoline( + enter_host_polymorphic_i, + ctx as *const _, + &native_param_types, + ); + } + } + let ptr = builder .insert_global() .expect("cannot bump-allocate global trampoline memory"); diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index 32290341f..5903508fc 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -205,6 +205,7 @@ pub struct X64FunctionCode { signatures: Arc>, function_signatures: Arc>, + signature: FuncSig, fsm: FunctionStateMap, offset: usize, @@ -713,11 +714,22 @@ impl ModuleCodeGenerator machine.track_state = self.config.as_ref().unwrap().track_state; assembler.emit_label(begin_label); + + let signatures = self.signatures.as_ref().unwrap(); + let function_signatures = self.function_signatures.as_ref().unwrap(); + let sig_index = function_signatures + .get(FuncIndex::new( + self.functions.len() + self.func_import_count, + )) + .unwrap() + .clone(); + let sig = signatures.get(sig_index).unwrap().clone(); let code = X64FunctionCode { local_function_id: self.functions.len(), - signatures: self.signatures.as_ref().unwrap().clone(), - function_signatures: self.function_signatures.as_ref().unwrap().clone(), + signatures: signatures.clone(), + function_signatures: function_signatures.clone(), + signature: sig, fsm: FunctionStateMap::new(new_machine_state(), self.functions.len(), 32, vec![]), // only a placeholder; this is initialized later in `begin_body` offset: begin_offset.0, @@ -6347,7 +6359,14 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + match return_types[0] { + WpType::F32 | WpType::F64 => { + a.emit_mov(Size::S64, Location::XMM(XMM::XMM0), ret); + } + _ => { + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + } } } Operator::CallIndirect { index, table_index } => { @@ -6486,7 +6505,14 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + match return_types[0] { + WpType::F32 | WpType::F64 => { + a.emit_mov(Size::S64, Location::XMM(XMM::XMM0), ret); + } + _ => { + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + } } } Operator::If { ty } => { @@ -7701,6 +7727,18 @@ impl FunctionCodeGenerator for X64FunctionCode { self.machine.finalize_locals(a, &self.locals); a.emit_mov(Size::S64, Location::GPR(GPR::RBP), Location::GPR(GPR::RSP)); a.emit_pop(Size::S64, Location::GPR(GPR::RBP)); + + // Make a copy of the return value in XMM0, as required by the SysV CC. + match self.signature.returns() { + [x] if *x == Type::F32 || *x == Type::F64 => { + a.emit_mov( + Size::S64, + Location::GPR(GPR::RAX), + Location::XMM(XMM::XMM0), + ); + } + _ => {} + } a.emit_ret(); } else { let released = &self.value_stack[frame.value_stack_depth..];