winch_codegen/codegen/
builtin.rs1use 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 Builtin(BuiltinFunctionIndex),
17 LibCall(LibCall),
20}
21
22impl BuiltinType {
23 pub fn builtin(idx: BuiltinFunctionIndex) -> Self {
26 Self::Builtin(idx)
27 }
28
29 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
51pub struct BuiltinFunctionInner {
53 sig: ABISig,
55 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 pub struct BuiltinFunctions {
69 host_call_conv: CallingConvention,
71 wasm_call_conv: CallingConvention,
73 ptr_type: WasmValType,
75 ceil_f32: Option<BuiltinFunction>,
77 ceil_f64: Option<BuiltinFunction>,
79 floor_f32: Option<BuiltinFunction>,
81 floor_f64: Option<BuiltinFunction>,
83 trunc_f32: Option<BuiltinFunction>,
85 trunc_f64: Option<BuiltinFunction>,
87 nearest_f32: Option<BuiltinFunction>,
89 nearest_f64: Option<BuiltinFunction>,
91 $(
92 $( #[ $attr ] )*
93 $name: Option<BuiltinFunction>,
94 )*
95 }
96
97 #[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(¶ms, &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);