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