1 //! Naming well-known routines in the runtime library.
2 
3 use crate::ir::{
4     types, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, Inst, 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 `libcall` in `func`, following the signature
170 /// for `inst`.
171 ///
172 /// If there is an existing reference, use it, otherwise make a new one.
173 pub(crate) fn get_libcall_funcref(
174     libcall: LibCall,
175     call_conv: CallConv,
176     func: &mut Function,
177     inst: Inst,
178     isa: &dyn TargetIsa,
179 ) -> FuncRef {
180     find_funcref(libcall, func)
181         .unwrap_or_else(|| make_funcref_for_inst(libcall, call_conv, func, inst, isa))
182 }
183 
184 /// Get a function reference for the probestack function in `func`.
185 ///
186 /// If there is an existing reference, use it, otherwise make a new one.
187 pub fn get_probestack_funcref(
188     func: &mut Function,
189     reg_type: Type,
190     arg_reg: RegUnit,
191     isa: &dyn TargetIsa,
192 ) -> FuncRef {
193     find_funcref(LibCall::Probestack, func)
194         .unwrap_or_else(|| make_funcref_for_probestack(func, reg_type, arg_reg, isa))
195 }
196 
197 /// Get the existing function reference for `libcall` in `func` if it exists.
198 fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
199     // We're assuming that all libcall function decls are at the end.
200     // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless.
201     for (fref, func_data) in func.dfg.ext_funcs.iter().rev() {
202         match func_data.name {
203             ExternalName::LibCall(lc) => {
204                 if lc == libcall {
205                     return Some(fref);
206                 }
207             }
208             _ => break,
209         }
210     }
211     None
212 }
213 
214 /// Create a funcref for `LibCall::Probestack`.
215 fn make_funcref_for_probestack(
216     func: &mut Function,
217     reg_type: Type,
218     arg_reg: RegUnit,
219     isa: &dyn TargetIsa,
220 ) -> FuncRef {
221     let mut sig = Signature::new(CallConv::Probestack);
222     let rax = AbiParam::special_reg(reg_type, ArgumentPurpose::Normal, arg_reg);
223     sig.params.push(rax);
224     if !isa.flags().probestack_func_adjusts_sp() {
225         sig.returns.push(rax);
226     }
227     make_funcref(LibCall::Probestack, func, sig, isa)
228 }
229 
230 /// Create a funcref for `libcall` with a signature matching `inst`.
231 fn make_funcref_for_inst(
232     libcall: LibCall,
233     call_conv: CallConv,
234     func: &mut Function,
235     inst: Inst,
236     isa: &dyn TargetIsa,
237 ) -> FuncRef {
238     let mut sig = Signature::new(call_conv);
239     for &v in func.dfg.inst_args(inst) {
240         sig.params.push(AbiParam::new(func.dfg.value_type(v)));
241     }
242     for &v in func.dfg.inst_results(inst) {
243         sig.returns.push(AbiParam::new(func.dfg.value_type(v)));
244     }
245 
246     if call_conv.extends_baldrdash() {
247         // Adds the special VMContext parameter to the signature.
248         sig.params.push(AbiParam::special(
249             isa.pointer_type(),
250             ArgumentPurpose::VMContext,
251         ));
252     }
253 
254     make_funcref(libcall, func, sig, isa)
255 }
256 
257 /// Create a funcref for `libcall`.
258 fn make_funcref(
259     libcall: LibCall,
260     func: &mut Function,
261     sig: Signature,
262     isa: &dyn TargetIsa,
263 ) -> FuncRef {
264     let sigref = func.import_signature(sig);
265 
266     func.import_function(ExtFuncData {
267         name: ExternalName::LibCall(libcall),
268         signature: sigref,
269         colocated: isa.flags().use_colocated_libcalls(),
270     })
271 }
272 
273 #[cfg(test)]
274 mod tests {
275     use super::*;
276     use alloc::string::ToString;
277 
278     #[test]
279     fn display() {
280         assert_eq!(LibCall::CeilF32.to_string(), "CeilF32");
281         assert_eq!(LibCall::NearestF64.to_string(), "NearestF64");
282     }
283 
284     #[test]
285     fn parsing() {
286         assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32));
287     }
288 }
289