use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::values::BasicValueEnum;
use crate::error::LoweringError;
use crate::func::{mangled_subroutine_name, subroutine_abi};
fn int_type(ctx: &Context, w: u32) -> inkwell::types::IntType<'_> {
ctx.custom_width_int_type(std::num::NonZeroU32::new(w).expect("zero-width int type"))
.expect("custom_width_int_type")
}
pub fn resolver_symbol(sym: &sv_lang::Symbol<'_>) -> Result<String, LoweringError> {
Ok(format!("sv_nettype_res_{}", mangled_subroutine_name(sym)?))
}
pub type ResolverFn = unsafe extern "C" fn(u64, *mut u64);
pub(crate) fn emit_resolver<'ctx>(
ctx: &'ctx Context,
module: &Module<'ctx>,
sym: &sv_lang::Symbol<'_>,
) -> Result<(), LoweringError> {
let name = resolver_symbol(sym)?;
if module.get_function(&name).is_some() {
return Ok(());
}
let err = |m: &str| LoweringError::unsupported_at(m, sym);
let out_words = result_words(sym)
.ok_or_else(|| err("a nettype resolution function whose result has no stored form"))?;
let abi = subroutine_abi(sym);
if !abi.signals.is_empty() || abi.needs_handle {
return Err(err(
"a nettype resolution function that reads or writes module-level state",
));
}
let mangled = mangled_subroutine_name(sym)?;
let callee = module
.get_function(&mangled)
.ok_or_else(|| err("a nettype resolution function that was never lowered"))?;
if callee.count_params() != 1 {
return Err(err(
"a nettype resolution function must take exactly one dynamic-array argument",
));
}
let i64_ty = ctx.i64_type();
let ptr_ty = ctx.ptr_type(inkwell::AddressSpace::default());
let f = module.add_function(
&name,
ctx.void_type()
.fn_type(&[i64_ty.into(), ptr_ty.into()], false),
None,
);
let b = ctx.create_builder();
b.position_at_end(ctx.append_basic_block(f, "entry"));
let handle = f.get_nth_param(0).unwrap().into_int_value();
let out = f.get_nth_param(1).unwrap().into_pointer_value();
let arg = match callee.get_type().get_param_types()[0] {
inkwell::types::BasicMetadataTypeEnum::IntType(t) => b
.build_int_z_extend_or_bit_cast(handle, t, "handle")
.map_err(|_| err("a resolution function argument of the wrong type"))?,
_ => {
return Err(err(
"a nettype resolution function argument that is not a dynamic array",
));
}
};
let call = b.build_call(callee, &[arg.into()], "res").unwrap();
let ret = call
.try_as_basic_value()
.basic()
.ok_or_else(|| err("a nettype resolution function with no result"))?;
let out_ty = int_type(ctx, 64 * out_words);
let bits = match ret {
BasicValueEnum::FloatValue(v) => {
let w = match v.get_type().get_bit_width() {
32 => 32,
_ => 64,
};
let bits = b
.build_bit_cast(v, int_type(ctx, w), "res_bits")
.unwrap()
.into_int_value();
b.build_int_z_extend(bits, out_ty, "res_ext").unwrap()
}
BasicValueEnum::IntValue(v) => {
if v.get_type().get_bit_width() > 64 * out_words {
return Err(err(
"a nettype resolution function result wider than the nettype's data type",
));
}
b.build_int_z_extend_or_bit_cast(v, out_ty, "res_ext")
.unwrap()
}
_ => {
return Err(err(
"a nettype resolution function result of an unsupported type",
));
}
};
let store = b.build_store(out, bits).unwrap();
store.set_alignment(8).unwrap();
b.build_return(None).unwrap();
Ok(())
}
pub fn result_words(sym: &sv_lang::Symbol<'_>) -> Option<u32> {
let ty = sym.subroutine_return_type()?;
let w = sv_sim_sim::state::storage::storage_width(&ty)?;
Some((2 * w).div_ceil(64) as u32)
}