use crate::{
abi::{ABISig, ABI},
codegen::env::ptr_type_from_ptr_size,
CallingConvention,
};
use cranelift_codegen::ir::LibCall;
use std::sync::Arc;
use wasmtime_environ::{BuiltinFunctionIndex, PtrSize, VMOffsets, WasmValType};
#[derive(Copy, Clone)]
pub(crate) enum BuiltinType {
Dynamic {
index: u32,
base: u32,
},
Known(LibCall),
}
impl BuiltinType {
pub fn dynamic(index: u32, base: u32) -> Self {
Self::Dynamic { index, base }
}
pub fn known(libcall: LibCall) -> Self {
Self::Known(libcall)
}
}
#[derive(Clone)]
pub struct BuiltinFunction {
inner: Arc<BuiltinFunctionInner>,
}
impl BuiltinFunction {
pub(crate) fn sig(&self) -> &ABISig {
&self.inner.sig
}
pub(crate) fn ty(&self) -> BuiltinType {
self.inner.ty
}
}
pub struct BuiltinFunctionInner {
sig: ABISig,
ty: BuiltinType,
}
macro_rules! declare_function_sig {
(
$(
$( #[$attr:meta] )*
$name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
)*
) => {
pub struct BuiltinFunctions {
call_conv: CallingConvention,
ptr_size: u8,
ptr_type: WasmValType,
base: u32,
ceil_f32: Option<BuiltinFunction>,
ceil_f64: Option<BuiltinFunction>,
floor_f32: Option<BuiltinFunction>,
floor_f64: Option<BuiltinFunction>,
trunc_f32: Option<BuiltinFunction>,
trunc_f64: Option<BuiltinFunction>,
nearest_f32: Option<BuiltinFunction>,
nearest_f64: Option<BuiltinFunction>,
$(
$name: Option<BuiltinFunction>,
)*
}
#[allow(dead_code)]
impl BuiltinFunctions {
pub fn new<P: PtrSize>(vmoffsets: &VMOffsets<P>, call_conv: CallingConvention) -> Self {
let size = vmoffsets.ptr.size();
Self {
ptr_size: size,
call_conv,
base: vmoffsets.vmctx_builtin_functions(),
ptr_type: ptr_type_from_ptr_size(size),
ceil_f32: None,
ceil_f64: None,
floor_f32: None,
floor_f64: None,
trunc_f32: None,
trunc_f64: None,
nearest_f32: None,
nearest_f64: None,
$(
$name: None,
)*
}
}
fn pointer(&self) -> WasmValType {
self.ptr_type
}
fn vmctx(&self) -> WasmValType {
self.pointer()
}
fn i32(&self) -> WasmValType {
WasmValType::I32
}
fn f32(&self) -> WasmValType {
WasmValType::F32
}
fn f64(&self) -> WasmValType {
WasmValType::F64
}
fn i64(&self) -> WasmValType {
WasmValType::I64
}
fn reference(&self) -> WasmValType {
self.pointer()
}
fn over_f64<A: ABI>(&self) -> ABISig {
A::sig_from(&[self.f64()], &[self.f64()], &self.call_conv)
}
fn over_f32<A: ABI>(&self) -> ABISig {
A::sig_from(&[self.f64()], &[self.f64()], &self.call_conv)
}
pub(crate) fn ceil_f32<A: ABI>(&mut self) -> BuiltinFunction {
if self.ceil_f32.is_none() {
let sig = self.over_f32::<A>();
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::CeilF32) });
self.ceil_f32 = Some(BuiltinFunction {
inner,
});
}
self.ceil_f32.as_ref().unwrap().clone()
}
pub(crate) fn ceil_f64<A: ABI>(&mut self) -> BuiltinFunction {
if self.ceil_f64.is_none() {
let sig = self.over_f64::<A>();
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::CeilF64) });
self.ceil_f64 = Some(BuiltinFunction {
inner,
});
}
self.ceil_f64.as_ref().unwrap().clone()
}
pub(crate) fn floor_f32<A: ABI>(&mut self) -> BuiltinFunction {
if self.floor_f32.is_none() {
let sig = self.over_f32::<A>();
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::FloorF32) });
self.floor_f32 = Some(BuiltinFunction {
inner,
});
}
self.floor_f32.as_ref().unwrap().clone()
}
pub(crate) fn floor_f64<A: ABI>(&mut self) -> BuiltinFunction {
if self.floor_f64.is_none() {
let sig = self.over_f64::<A>();
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::FloorF64) });
self.floor_f64 = Some(BuiltinFunction {
inner,
});
}
self.floor_f64.as_ref().unwrap().clone()
}
pub(crate) fn trunc_f32<A: ABI>(&mut self) -> BuiltinFunction {
if self.trunc_f32.is_none() {
let sig = self.over_f32::<A>();
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::TruncF32) });
self.trunc_f32 = Some(BuiltinFunction {
inner,
});
}
self.trunc_f32.as_ref().unwrap().clone()
}
pub(crate) fn trunc_f64<A: ABI>(&mut self) -> BuiltinFunction {
if self.trunc_f64.is_none() {
let sig = self.over_f64::<A>();
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::TruncF64) });
self.trunc_f64 = Some(BuiltinFunction {
inner,
});
}
self.trunc_f64.as_ref().unwrap().clone()
}
pub(crate) fn nearest_f32<A: ABI>(&mut self) -> BuiltinFunction {
if self.nearest_f32.is_none() {
let sig = self.over_f32::<A>();
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::NearestF32) });
self.nearest_f32 = Some(BuiltinFunction {
inner,
});
}
self.nearest_f32.as_ref().unwrap().clone()
}
pub(crate) fn nearest_f64<A: ABI>(&mut self) -> BuiltinFunction {
if self.nearest_f64.is_none() {
let sig = self.over_f64::<A>();
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::NearestF64) });
self.nearest_f64 = Some(BuiltinFunction {
inner,
});
}
self.nearest_f64.as_ref().unwrap().clone()
}
$(
pub(crate) fn $name<A: ABI, P: PtrSize>(&mut self) -> BuiltinFunction {
if self.$name.is_none() {
let params = vec![ $(self.$param() ),* ];
let result = vec![ $(self.$result() )?];
let sig = A::sig_from(¶ms, &result, &self.call_conv);
let index = BuiltinFunctionIndex::$name();
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::dynamic(index.index() * (self.ptr_size as u32), self.base) });
self.$name = Some(BuiltinFunction {
inner,
});
}
self.$name.as_ref().unwrap().clone()
}
)*
}
}
}
wasmtime_environ::foreach_builtin_function!(declare_function_sig);