1 //! Naming well-known routines in the runtime library.
2 
3 use crate::ir::{types, AbiParam, ExternalName, FuncRef, Function, Opcode, Signature, Type};
4 use crate::isa::CallConv;
5 use core::fmt;
6 use core::str::FromStr;
7 #[cfg(feature = "enable-serde")]
8 use serde::{Deserialize, Serialize};
9 
10 /// The name of a runtime library routine.
11 ///
12 /// Runtime library calls are generated for Cranelift IR instructions that don't have an equivalent
13 /// ISA instruction or an easy macro expansion. A `LibCall` is used as a well-known name to refer to
14 /// the runtime library routine. This way, Cranelift doesn't have to know about the naming
15 /// convention in the embedding VM's runtime library.
16 ///
17 /// This list is likely to grow over time.
18 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
19 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
20 pub enum LibCall {
21     /// probe for stack overflow. These are emitted for functions which need
22     /// when the `enable_probestack` setting is true.
23     Probestack,
24     /// udiv.i64
25     UdivI64,
26     /// sdiv.i64
27     SdivI64,
28     /// urem.i64
29     UremI64,
30     /// srem.i64
31     SremI64,
32     /// ishl.i64
33     IshlI64,
34     /// ushr.i64
35     UshrI64,
36     /// sshr.i64
37     SshrI64,
38     /// ceil.f32
39     CeilF32,
40     /// ceil.f64
41     CeilF64,
42     /// floor.f32
43     FloorF32,
44     /// floor.f64
45     FloorF64,
46     /// trunc.f32
47     TruncF32,
48     /// frunc.f64
49     TruncF64,
50     /// nearest.f32
51     NearestF32,
52     /// nearest.f64
53     NearestF64,
54     /// fma.f32
55     FmaF32,
56     /// fma.f64
57     FmaF64,
58     /// libc.memcpy
59     Memcpy,
60     /// libc.memset
61     Memset,
62     /// libc.memmove
63     Memmove,
64     /// libc.memcmp
65     Memcmp,
66 
67     /// Elf __tls_get_addr
68     ElfTlsGetAddr,
69     // When adding a new variant make sure to add it to `all_libcalls` too.
70 }
71 
72 impl fmt::Display for LibCall {
73     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74         fmt::Debug::fmt(self, f)
75     }
76 }
77 
78 impl FromStr for LibCall {
79     type Err = ();
80 
81     fn from_str(s: &str) -> Result<Self, Self::Err> {
82         match s {
83             "Probestack" => Ok(Self::Probestack),
84             "UdivI64" => Ok(Self::UdivI64),
85             "SdivI64" => Ok(Self::SdivI64),
86             "UremI64" => Ok(Self::UremI64),
87             "SremI64" => Ok(Self::SremI64),
88             "IshlI64" => Ok(Self::IshlI64),
89             "UshrI64" => Ok(Self::UshrI64),
90             "SshrI64" => Ok(Self::SshrI64),
91             "CeilF32" => Ok(Self::CeilF32),
92             "CeilF64" => Ok(Self::CeilF64),
93             "FloorF32" => Ok(Self::FloorF32),
94             "FloorF64" => Ok(Self::FloorF64),
95             "TruncF32" => Ok(Self::TruncF32),
96             "TruncF64" => Ok(Self::TruncF64),
97             "NearestF32" => Ok(Self::NearestF32),
98             "NearestF64" => Ok(Self::NearestF64),
99             "FmaF32" => Ok(Self::FmaF32),
100             "FmaF64" => Ok(Self::FmaF64),
101             "Memcpy" => Ok(Self::Memcpy),
102             "Memset" => Ok(Self::Memset),
103             "Memmove" => Ok(Self::Memmove),
104             "Memcmp" => Ok(Self::Memcmp),
105 
106             "ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr),
107             _ => Err(()),
108         }
109     }
110 }
111 
112 impl LibCall {
113     /// Get the well-known library call name to use as a replacement for an instruction with the
114     /// given opcode and controlling type variable.
115     ///
116     /// Returns `None` if no well-known library routine name exists for that instruction.
117     pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option<Self> {
118         Some(match ctrl_type {
119             types::I64 => match opcode {
120                 Opcode::Udiv => Self::UdivI64,
121                 Opcode::Sdiv => Self::SdivI64,
122                 Opcode::Urem => Self::UremI64,
123                 Opcode::Srem => Self::SremI64,
124                 Opcode::Ishl => Self::IshlI64,
125                 Opcode::Ushr => Self::UshrI64,
126                 Opcode::Sshr => Self::SshrI64,
127                 _ => return None,
128             },
129             types::F32 => match opcode {
130                 Opcode::Ceil => Self::CeilF32,
131                 Opcode::Floor => Self::FloorF32,
132                 Opcode::Trunc => Self::TruncF32,
133                 Opcode::Nearest => Self::NearestF32,
134                 Opcode::Fma => Self::FmaF32,
135                 _ => return None,
136             },
137             types::F64 => match opcode {
138                 Opcode::Ceil => Self::CeilF64,
139                 Opcode::Floor => Self::FloorF64,
140                 Opcode::Trunc => Self::TruncF64,
141                 Opcode::Nearest => Self::NearestF64,
142                 Opcode::Fma => Self::FmaF64,
143                 _ => return None,
144             },
145             _ => return None,
146         })
147     }
148 
149     /// Get a list of all known `LibCall`'s.
150     pub fn all_libcalls() -> &'static [LibCall] {
151         use LibCall::*;
152         &[
153             Probestack,
154             UdivI64,
155             SdivI64,
156             UremI64,
157             SremI64,
158             IshlI64,
159             UshrI64,
160             SshrI64,
161             CeilF32,
162             CeilF64,
163             FloorF32,
164             FloorF64,
165             TruncF32,
166             TruncF64,
167             NearestF32,
168             NearestF64,
169             FmaF32,
170             FmaF64,
171             Memcpy,
172             Memset,
173             Memmove,
174             Memcmp,
175             ElfTlsGetAddr,
176         ]
177     }
178 
179     /// Get a [Signature] for the function targeted by this [LibCall].
180     pub fn signature(&self, call_conv: CallConv) -> Signature {
181         use types::*;
182         let mut sig = Signature::new(call_conv);
183 
184         match self {
185             LibCall::UdivI64
186             | LibCall::SdivI64
187             | LibCall::UremI64
188             | LibCall::SremI64
189             | LibCall::IshlI64
190             | LibCall::UshrI64
191             | LibCall::SshrI64 => {
192                 sig.params.push(AbiParam::new(I64));
193                 sig.params.push(AbiParam::new(I64));
194                 sig.returns.push(AbiParam::new(I64));
195             }
196             LibCall::CeilF32 | LibCall::FloorF32 | LibCall::TruncF32 | LibCall::NearestF32 => {
197                 sig.params.push(AbiParam::new(F32));
198                 sig.returns.push(AbiParam::new(F32));
199             }
200             LibCall::TruncF64 | LibCall::FloorF64 | LibCall::CeilF64 | LibCall::NearestF64 => {
201                 sig.params.push(AbiParam::new(F64));
202                 sig.returns.push(AbiParam::new(F64));
203             }
204             LibCall::FmaF32 | LibCall::FmaF64 => {
205                 let ty = if *self == LibCall::FmaF32 { F32 } else { F64 };
206 
207                 sig.params.push(AbiParam::new(ty));
208                 sig.params.push(AbiParam::new(ty));
209                 sig.params.push(AbiParam::new(ty));
210                 sig.returns.push(AbiParam::new(ty));
211             }
212             LibCall::Probestack
213             | LibCall::Memcpy
214             | LibCall::Memset
215             | LibCall::Memmove
216             | LibCall::Memcmp
217             | LibCall::ElfTlsGetAddr => unimplemented!(),
218         }
219 
220         sig
221     }
222 }
223 
224 /// Get a function reference for the probestack function in `func`.
225 ///
226 /// If there is an existing reference, use it, otherwise make a new one.
227 pub fn get_probestack_funcref(func: &mut Function) -> Option<FuncRef> {
228     find_funcref(LibCall::Probestack, func)
229 }
230 
231 /// Get the existing function reference for `libcall` in `func` if it exists.
232 fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
233     // We're assuming that all libcall function decls are at the end.
234     // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless.
235     for (fref, func_data) in func.dfg.ext_funcs.iter().rev() {
236         match func_data.name {
237             ExternalName::LibCall(lc) => {
238                 if lc == libcall {
239                     return Some(fref);
240                 }
241             }
242             _ => break,
243         }
244     }
245     None
246 }
247 
248 #[cfg(test)]
249 mod tests {
250     use super::*;
251     use alloc::string::ToString;
252 
253     #[test]
254     fn display() {
255         assert_eq!(LibCall::CeilF32.to_string(), "CeilF32");
256         assert_eq!(LibCall::NearestF64.to_string(), "NearestF64");
257     }
258 
259     #[test]
260     fn parsing() {
261         assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32));
262     }
263 
264     #[test]
265     fn all_libcalls_to_from_string() {
266         for &libcall in LibCall::all_libcalls() {
267             assert_eq!(libcall.to_string().parse(), Ok(libcall));
268         }
269     }
270 }
271