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