koopa 0.0.6

Library for generating/parsing/optimizing Koopa IR.
Documentation
use super::interpreter::{new_error, Val};
use koopa::ir::{FunctionData, Type, TypeKind};
use libloading::{Error, Library, Symbol};
use std::ffi::{CString, OsStr};
use std::io::Result as IoResult;
use std::mem::transmute;
use std::ptr::NonNull;

pub struct ExternFuncs {
  libs: Vec<Library>,
}

impl ExternFuncs {
  pub unsafe fn new<T: AsRef<OsStr>>(lib_files: &[T]) -> Result<Self, Error> {
    Ok(Self {
      libs: lib_files
        .iter()
        .map(|f| Library::new(f))
        .collect::<Result<_, _>>()?,
    })
  }

  pub unsafe fn call(&mut self, func: &FunctionData, args: Vec<Val>) -> IoResult<Val> {
    assert!(
      func.layout().bbs().is_empty(),
      "expected function declaration"
    );
    assert!(!func.name().is_empty(), "invalid function name");
    let name = &func.name()[1..];
    let ret_ty = match func.ty().kind() {
      TypeKind::Function(_, ret) => ret,
      _ => panic!("invalid function"),
    };
    let sym_name = CString::new(name).map_err(|e| new_error(&format!("{}", e)))?;
    let sym = self
      .libs
      .iter()
      .find_map(|l| l.get(sym_name.to_bytes_with_nul()).ok())
      .ok_or_else(|| new_error(&format!("external function '{}' not found", name)))?;
    Self::call_ext_func(sym, args, ret_ty)
  }

  unsafe fn call_ext_func(
    sym: Symbol<'_, *const ()>,
    args: Vec<Val>,
    ret_ty: &Type,
  ) -> IoResult<Val> {
    macro_rules! call_func_ptr {
      ($fp:expr, $args:expr, $($ty:ident)*) => {
        call_func_ptr!(@args
          transmute::<*const (), unsafe extern "C" fn($(call_func_ptr!(@subst $ty)),*) -> usize>($fp),
          $args, 0, () $(,$ty)*
        )
      };
      (@subst $id:ident) => { usize };
      (@args $func:expr, $args:expr, $i:expr, ($($t:tt)*)) => {
        call_func_ptr!(@call $func, $($t)*)
      };
      (@args $func:expr, $args:expr, $i:expr, ($($t:tt)*), $head:ident $(,$tail:ident)*) => {
        call_func_ptr!(@args
          $func, $args, $i + 1, ($($t)* Self::val_to_usize(&$args[$i])?,) $(,$tail)*
        )
      };
      (@call $func:expr, $($args:tt)*) => {
        $func($($args)*)
      };
    }
    let func_ptr = *sym.clone();
    let ret = match args.len() {
      0 => call_func_ptr!(func_ptr, args,),
      1 => call_func_ptr!(func_ptr, args, A),
      2 => call_func_ptr!(func_ptr, args, A A),
      3 => call_func_ptr!(func_ptr, args, A A A),
      4 => call_func_ptr!(func_ptr, args, A A A A),
      5 => call_func_ptr!(func_ptr, args, A A A A A),
      6 => call_func_ptr!(func_ptr, args, A A A A A A),
      7 => call_func_ptr!(func_ptr, args, A A A A A A A),
      8 => call_func_ptr!(func_ptr, args, A A A A A A A A),
      9 => call_func_ptr!(func_ptr, args, A A A A A A A A A),
      10 => call_func_ptr!(func_ptr, args, A A A A A A A A A A),
      11 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A),
      12 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A),
      13 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A),
      14 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A),
      15 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A),
      16 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A),
      17 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A),
      18 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A),
      19 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A A),
      20 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A A A),
      21 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A A A A),
      22 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A A A A A),
      23 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A A A A A A),
      24 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A A A A A A A),
      25 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A A A A A A A A),
      26 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A A A A A A A A A),
      27 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A A A A A A A A A A),
      28 => call_func_ptr!(func_ptr, args, A A A A A A A A A A A A A A A A A A A A A A A A A A A A),
      _ => {
        return Err(new_error(
          "argument number exceeded in external function call",
        ))
      }
    };
    Self::usize_to_val(ret, ret_ty)
  }

  fn val_to_usize(val: &Val) -> IoResult<usize> {
    match val {
      Val::Undef => Ok(0),
      Val::Int(i) => Ok(*i as usize),
      Val::Pointer { ptr: Some(p), .. } => Ok(p.as_ptr() as usize),
      Val::Pointer { ptr: None, .. } => Ok(0),
      Val::UnsafePointer(Some(p)) => Ok(p.as_ptr() as usize),
      Val::UnsafePointer(None) => Ok(0),
      _ => Err(new_error("unsupported value")),
    }
  }

  fn usize_to_val(u: usize, ty: &Type) -> IoResult<Val> {
    match ty.kind() {
      TypeKind::Int32 => Ok(Val::Int(u as i32)),
      TypeKind::Unit => Ok(Val::Undef),
      TypeKind::Pointer(_) => Ok(Val::UnsafePointer(NonNull::new(u as *mut ()))),
      _ => Err(new_error("unsupported value type")),
    }
  }
}