1 //! Naming well-known routines in the runtime library.
2 
3 use crate::{
4     ir::{AbiParam, ExternalName, FuncRef, Function, Signature, Type, types},
5     isa::CallConv,
6 };
7 use core::fmt;
8 use core::str::FromStr;
9 #[cfg(feature = "enable-serde")]
10 use serde_derive::{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     /// trunc.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 
60     /// The `pshufb` on x86 when SSSE3 isn't available.
61     X86Pshufb,
62     // When adding a new variant make sure to add it to `all_libcalls` too.
63 }
64 
65 impl fmt::Display for LibCall {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result66     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67         fmt::Debug::fmt(self, f)
68     }
69 }
70 
71 impl FromStr for LibCall {
72     type Err = ();
73 
from_str(s: &str) -> Result<Self, Self::Err>74     fn from_str(s: &str) -> Result<Self, Self::Err> {
75         match s {
76             "Probestack" => Ok(Self::Probestack),
77             "CeilF32" => Ok(Self::CeilF32),
78             "CeilF64" => Ok(Self::CeilF64),
79             "FloorF32" => Ok(Self::FloorF32),
80             "FloorF64" => Ok(Self::FloorF64),
81             "TruncF32" => Ok(Self::TruncF32),
82             "TruncF64" => Ok(Self::TruncF64),
83             "NearestF32" => Ok(Self::NearestF32),
84             "NearestF64" => Ok(Self::NearestF64),
85             "FmaF32" => Ok(Self::FmaF32),
86             "FmaF64" => Ok(Self::FmaF64),
87             "Memcpy" => Ok(Self::Memcpy),
88             "Memset" => Ok(Self::Memset),
89             "Memmove" => Ok(Self::Memmove),
90             "Memcmp" => Ok(Self::Memcmp),
91 
92             "ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr),
93             "ElfTlsGetOffset" => Ok(Self::ElfTlsGetOffset),
94 
95             "X86Pshufb" => Ok(Self::X86Pshufb),
96             _ => Err(()),
97         }
98     }
99 }
100 
101 impl LibCall {
102     /// Get a list of all known `LibCall`'s.
all_libcalls() -> &'static [LibCall]103     pub fn all_libcalls() -> &'static [LibCall] {
104         use LibCall::*;
105         &[
106             Probestack,
107             CeilF32,
108             CeilF64,
109             FloorF32,
110             FloorF64,
111             TruncF32,
112             TruncF64,
113             NearestF32,
114             NearestF64,
115             FmaF32,
116             FmaF64,
117             Memcpy,
118             Memset,
119             Memmove,
120             Memcmp,
121             ElfTlsGetAddr,
122             ElfTlsGetOffset,
123             X86Pshufb,
124         ]
125     }
126 
127     /// Get a [Signature] for the function targeted by this [LibCall].
signature(&self, call_conv: CallConv, pointer_type: Type) -> Signature128     pub fn signature(&self, call_conv: CallConv, pointer_type: Type) -> Signature {
129         use types::*;
130         let mut sig = Signature::new(call_conv);
131 
132         match self {
133             LibCall::CeilF32 | LibCall::FloorF32 | LibCall::TruncF32 | LibCall::NearestF32 => {
134                 sig.params.push(AbiParam::new(F32));
135                 sig.returns.push(AbiParam::new(F32));
136             }
137             LibCall::TruncF64 | LibCall::FloorF64 | LibCall::CeilF64 | LibCall::NearestF64 => {
138                 sig.params.push(AbiParam::new(F64));
139                 sig.returns.push(AbiParam::new(F64));
140             }
141             LibCall::FmaF32 | LibCall::FmaF64 => {
142                 let ty = if *self == LibCall::FmaF32 { F32 } else { F64 };
143 
144                 sig.params.push(AbiParam::new(ty));
145                 sig.params.push(AbiParam::new(ty));
146                 sig.params.push(AbiParam::new(ty));
147                 sig.returns.push(AbiParam::new(ty));
148             }
149             LibCall::Memcpy | LibCall::Memmove => {
150                 // void* memcpy(void *dest, const void *src, size_t count);
151                 // void* memmove(void* dest, const void* src, size_t count);
152                 sig.params.push(AbiParam::new(pointer_type));
153                 sig.params.push(AbiParam::new(pointer_type));
154                 sig.params.push(AbiParam::new(pointer_type));
155                 sig.returns.push(AbiParam::new(pointer_type));
156             }
157             LibCall::Memset => {
158                 // void *memset(void *dest, int ch, size_t count);
159                 sig.params.push(AbiParam::new(pointer_type));
160                 sig.params.push(AbiParam::new(I32));
161                 sig.params.push(AbiParam::new(pointer_type));
162                 sig.returns.push(AbiParam::new(pointer_type));
163             }
164             LibCall::Memcmp => {
165                 // void* memcpy(void *dest, const void *src, size_t count);
166                 sig.params.push(AbiParam::new(pointer_type));
167                 sig.params.push(AbiParam::new(pointer_type));
168                 sig.params.push(AbiParam::new(pointer_type));
169                 sig.returns.push(AbiParam::new(I32))
170             }
171 
172             LibCall::Probestack | LibCall::ElfTlsGetAddr | LibCall::ElfTlsGetOffset => {
173                 unimplemented!()
174             }
175             LibCall::X86Pshufb => {
176                 sig.params.push(AbiParam::new(I8X16));
177                 sig.params.push(AbiParam::new(I8X16));
178                 sig.returns.push(AbiParam::new(I8X16));
179             }
180         }
181 
182         sig
183     }
184 }
185 
186 /// Get a function reference for the probestack function in `func`.
187 ///
188 /// If there is an existing reference, use it, otherwise make a new one.
get_probestack_funcref(func: &mut Function) -> Option<FuncRef>189 pub fn get_probestack_funcref(func: &mut Function) -> Option<FuncRef> {
190     find_funcref(LibCall::Probestack, func)
191 }
192 
193 /// Get the existing function reference for `libcall` in `func` if it exists.
find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef>194 fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
195     // We're assuming that all libcall function decls are at the end.
196     // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless.
197     for (fref, func_data) in func.dfg.ext_funcs.iter().rev() {
198         match func_data.name {
199             ExternalName::LibCall(lc) => {
200                 if lc == libcall {
201                     return Some(fref);
202                 }
203             }
204             _ => break,
205         }
206     }
207     None
208 }
209 
210 #[cfg(test)]
211 mod tests {
212     use super::*;
213     use alloc::string::ToString;
214 
215     #[test]
display()216     fn display() {
217         assert_eq!(LibCall::CeilF32.to_string(), "CeilF32");
218         assert_eq!(LibCall::NearestF64.to_string(), "NearestF64");
219     }
220 
221     #[test]
parsing()222     fn parsing() {
223         assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32));
224     }
225 
226     #[test]
all_libcalls_to_from_string()227     fn all_libcalls_to_from_string() {
228         for &libcall in LibCall::all_libcalls() {
229             assert_eq!(libcall.to_string().parse(), Ok(libcall));
230         }
231     }
232 }
233