use inkwell::values::{BasicMetadataValueEnum, BasicValueEnum};
use relon_ir::ir::IrType;
use crate::codegen::Emit;
use crate::error::LlvmError;
impl<'ctx, 'b, 'cp> Emit<'ctx, 'b, 'cp> {
pub(crate) fn emit_call_native_wasi(
&mut self,
import_idx: u32,
n: usize,
ret_ty: IrType,
) -> Result<(), LlvmError> {
let import = self.imports.get(import_idx as usize).ok_or_else(|| {
LlvmError::Codegen(format!(
"CallNative (wasm import) import_idx {import_idx} out of range"
))
})?;
let i32_t = self.ctx.i32_type();
let i64_t = self.ctx.i64_type();
let import_fn = match self.module.get_function(&import.name) {
Some(f) => f,
None => {
let params: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> =
import.param_tys.iter().map(|_| i64_t.into()).collect();
let fn_ty = match import.ret_ty {
IrType::Unit => self.ctx.void_type().fn_type(¶ms, false),
_ => i64_t.fn_type(¶ms, false),
};
self.module.add_function(
&import.name,
fn_ty,
Some(inkwell::module::Linkage::External),
)
}
};
let mut args: Vec<inkwell::values::IntValue<'ctx>> = Vec::with_capacity(n);
for _ in 0..n {
args.push(self.pop_int("CallNative (wasm import) arg")?);
}
args.reverse();
let mut call_args: Vec<BasicMetadataValueEnum<'ctx>> = Vec::with_capacity(n);
for (i, v) in args.iter().enumerate() {
let w = v.get_type().get_bit_width();
let v64 = if w == 64 {
*v
} else if w < 64 {
self.builder
.build_int_z_extend(*v, i64_t, &self.next_name("wasi_arg_zext"))
.map_err(|e| LlvmError::Codegen(format!("CallNative wasm arg{i} zext: {e}")))?
} else {
return Err(LlvmError::Codegen(format!(
"CallNative wasm arg{i} has i{w} width outside the i64 lane"
)));
};
call_args.push(v64.into());
}
let call_site = self
.builder
.build_call(import_fn, &call_args, &self.next_name("wasi_call"))
.map_err(|e| LlvmError::Codegen(format!("CallNative wasm build_call: {e}")))?;
match ret_ty {
IrType::Unit => {}
IrType::I64 => {
let result = match call_site.try_as_basic_value() {
inkwell::values::ValueKind::Basic(BasicValueEnum::IntValue(v)) => v,
other => {
return Err(LlvmError::Codegen(format!(
"CallNative wasm import returned {other:?}, expected i64"
)));
}
};
self.push(result, IrType::I64);
}
IrType::Bool => {
let result = match call_site.try_as_basic_value() {
inkwell::values::ValueKind::Basic(BasicValueEnum::IntValue(v)) => v,
other => {
return Err(LlvmError::Codegen(format!(
"CallNative wasm import returned {other:?}, expected i64"
)));
}
};
let b = self
.builder
.build_int_truncate(result, i32_t, &self.next_name("wasi_ret_bool"))
.map_err(|e| LlvmError::Codegen(format!("CallNative wasm ret trunc: {e}")))?;
self.push(b, IrType::Bool);
}
other => {
return Err(LlvmError::Codegen(format!(
"CallNative wasm ret_ty {other:?} unreachable after envelope check"
)));
}
}
Ok(())
}
}