winch_codegen/codegen/
builtin.rs

1//! Builtin function handling.
2
3use crate::{
4    abi::{ABISig, ABI},
5    codegen::env::ptr_type_from_ptr_size,
6    CallingConvention,
7};
8use anyhow::Result;
9use cranelift_codegen::ir::LibCall;
10use std::sync::Arc;
11use wasmtime_environ::{BuiltinFunctionIndex, PtrSize, VMOffsets, WasmValType};
12
13#[derive(Copy, Clone)]
14pub(crate) enum BuiltinType {
15    /// Dynamic built-in function, derived from the VMContext.
16    Builtin(BuiltinFunctionIndex),
17    /// A known libcall.
18    /// See [`cranelift_codegen::ir::LibCall`] for more details.
19    LibCall(LibCall),
20}
21
22impl BuiltinType {
23    /// Creates a new builtin from a Wasmtime-defined builtin function
24    /// enumerated with a [`BuiltinFunctionIndex`].
25    pub fn builtin(idx: BuiltinFunctionIndex) -> Self {
26        Self::Builtin(idx)
27    }
28
29    /// Creates a new builtin from a Compiler-defined [`LibCall`] typically used
30    /// late in lowering.
31    pub fn libcall(libcall: LibCall) -> Self {
32        Self::LibCall(libcall)
33    }
34}
35
36#[derive(Clone)]
37pub struct BuiltinFunction {
38    inner: Arc<BuiltinFunctionInner>,
39}
40
41impl BuiltinFunction {
42    pub(crate) fn sig(&self) -> &ABISig {
43        &self.inner.sig
44    }
45
46    pub(crate) fn ty(&self) -> BuiltinType {
47        self.inner.ty
48    }
49}
50
51/// Metadata about a builtin function.
52pub struct BuiltinFunctionInner {
53    /// The ABI specific signature of the function.
54    sig: ABISig,
55    /// The built-in function type.
56    ty: BuiltinType,
57}
58
59macro_rules! declare_function_sig {
60    (
61        $(
62            $( #[$attr:meta] )*
63            $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
64         )*
65    ) => {
66        /// Provides the ABI signatures for each builtin function
67        /// signature.
68        pub struct BuiltinFunctions {
69            /// The target calling convention for host intrinsics.
70            host_call_conv: CallingConvention,
71            /// The target calling convention for wasm builtins.
72            wasm_call_conv: CallingConvention,
73            /// The target pointer type, as a WebAssembly type.
74            ptr_type: WasmValType,
75            /// F32 Ceil.
76            ceil_f32: Option<BuiltinFunction>,
77            /// F64 Ceil.
78            ceil_f64: Option<BuiltinFunction>,
79            /// F32 Floor.
80            floor_f32: Option<BuiltinFunction>,
81            /// F64 Floor.
82            floor_f64: Option<BuiltinFunction>,
83            /// F32 Trunc.
84            trunc_f32: Option<BuiltinFunction>,
85            /// F64 Trunc.
86            trunc_f64: Option<BuiltinFunction>,
87            /// F32 Nearest.
88            nearest_f32: Option<BuiltinFunction>,
89            /// F64 Nearest.
90            nearest_f64: Option<BuiltinFunction>,
91            $(
92                $( #[ $attr ] )*
93                $name: Option<BuiltinFunction>,
94            )*
95        }
96
97        // Until all the builtin functions are used.
98        #[allow(dead_code)]
99        impl BuiltinFunctions {
100            pub fn new<P: PtrSize>(
101                vmoffsets: &VMOffsets<P>,
102                host_call_conv: CallingConvention,
103                wasm_call_conv: CallingConvention,
104            ) -> Self {
105                let size = vmoffsets.ptr.size();
106                #[allow(unused_doc_comments)]
107                Self {
108                    host_call_conv,
109                    wasm_call_conv,
110                    ptr_type: ptr_type_from_ptr_size(size),
111                    ceil_f32: None,
112                    ceil_f64: None,
113                    floor_f32: None,
114                    floor_f64: None,
115                    trunc_f32: None,
116                    trunc_f64: None,
117                    nearest_f32: None,
118                    nearest_f64: None,
119                    $(
120                        $( #[ $attr ] )*
121                        $name: None,
122                    )*
123                }
124            }
125
126            fn pointer(&self) -> WasmValType {
127                self.ptr_type
128            }
129
130            fn size(&self) -> WasmValType {
131                self.ptr_type
132            }
133
134            fn vmctx(&self) -> WasmValType {
135                self.pointer()
136            }
137
138            fn u32(&self) -> WasmValType {
139                WasmValType::I32
140            }
141
142            fn u8(&self) -> WasmValType {
143                WasmValType::I32
144            }
145
146            fn f32(&self) -> WasmValType {
147                WasmValType::F32
148            }
149
150            fn f64(&self) -> WasmValType {
151                WasmValType::F64
152            }
153
154            fn u64(&self) -> WasmValType {
155                WasmValType::I64
156            }
157
158            fn bool(&self) -> WasmValType {
159                WasmValType::I32
160            }
161
162            fn over_f64<A: ABI>(&self) -> Result<ABISig> {
163                A::sig_from(&[self.f64()], &[self.f64()], &self.host_call_conv)
164            }
165
166            fn over_f32<A: ABI>(&self) -> Result<ABISig> {
167                A::sig_from(&[self.f64()], &[self.f64()], &self.host_call_conv)
168            }
169
170            pub(crate) fn ceil_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
171                if self.ceil_f32.is_none() {
172                    let sig = self.over_f32::<A>()?;
173                    let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::CeilF32) });
174                    self.ceil_f32 = Some(BuiltinFunction {
175                        inner,
176                    });
177                }
178                Ok(self.ceil_f32.as_ref().unwrap().clone())
179            }
180
181            pub(crate) fn ceil_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
182                if self.ceil_f64.is_none() {
183                    let sig = self.over_f64::<A>()?;
184                    let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::CeilF64) });
185                    self.ceil_f64 = Some(BuiltinFunction {
186                        inner,
187                    });
188                }
189                Ok(self.ceil_f64.as_ref().unwrap().clone())
190            }
191
192            pub(crate) fn floor_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
193                if self.floor_f32.is_none() {
194                    let sig = self.over_f32::<A>()?;
195                    let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::FloorF32) });
196                    self.floor_f32 = Some(BuiltinFunction {
197                        inner,
198                    });
199                }
200                Ok(self.floor_f32.as_ref().unwrap().clone())
201            }
202
203            pub(crate) fn floor_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
204                if self.floor_f64.is_none() {
205                    let sig = self.over_f64::<A>()?;
206                    let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::FloorF64) });
207                    self.floor_f64 = Some(BuiltinFunction {
208                        inner,
209                    });
210                }
211                Ok(self.floor_f64.as_ref().unwrap().clone())
212            }
213
214            pub(crate) fn trunc_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
215                if self.trunc_f32.is_none() {
216                    let sig = self.over_f32::<A>()?;
217                    let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::TruncF32) });
218                    self.trunc_f32 = Some(BuiltinFunction {
219                        inner,
220                    });
221                }
222                Ok(self.trunc_f32.as_ref().unwrap().clone())
223            }
224
225            pub(crate) fn trunc_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
226                if self.trunc_f64.is_none() {
227                    let sig = self.over_f64::<A>()?;
228                    let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::TruncF64) });
229                    self.trunc_f64 = Some(BuiltinFunction {
230                        inner,
231                    });
232                }
233                Ok(self.trunc_f64.as_ref().unwrap().clone())
234            }
235
236            pub(crate) fn nearest_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
237                if self.nearest_f32.is_none() {
238                    let sig = self.over_f32::<A>()?;
239                    let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::NearestF32) });
240                    self.nearest_f32 = Some(BuiltinFunction {
241                        inner,
242                    });
243                }
244                Ok(self.nearest_f32.as_ref().unwrap().clone())
245            }
246
247            pub(crate) fn nearest_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
248                if self.nearest_f64.is_none() {
249                    let sig = self.over_f64::<A>()?;
250                    let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::NearestF64) });
251                    self.nearest_f64 = Some(BuiltinFunction {
252                        inner,
253                    });
254                }
255                Ok(self.nearest_f64.as_ref().unwrap().clone())
256            }
257
258            $(
259                $( #[ $attr ] )*
260                pub(crate) fn $name<A: ABI, P: PtrSize>(&mut self) -> Result<BuiltinFunction> {
261                    if self.$name.is_none() {
262                        let params = vec![ $(self.$param() ),* ];
263                        let result = vec![ $(self.$result() )?];
264                        let sig = A::sig_from(&params, &result, &self.wasm_call_conv)?;
265                        let index = BuiltinFunctionIndex::$name();
266                        let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::builtin(index) });
267                        self.$name = Some(BuiltinFunction {
268                            inner,
269                        });
270                    }
271
272                    Ok(self.$name.as_ref().unwrap().clone())
273                }
274             )*
275        }
276    }
277}
278
279wasmtime_environ::foreach_builtin_function!(declare_function_sig);