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