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