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     /// ceil.f32
36     CeilF32,
37     /// ceil.f64
38     CeilF64,
39     /// floor.f32
40     FloorF32,
41     /// floor.f64
42     FloorF64,
43     /// trunc.f32
44     TruncF32,
45     /// frunc.f64
46     TruncF64,
47     /// nearest.f32
48     NearestF32,
49     /// nearest.f64
50     NearestF64,
51     /// libc.memcpy
52     Memcpy,
53     /// libc.memset
54     Memset,
55     /// libc.memmove
56     Memmove,
57 
58     /// Elf __tls_get_addr
59     ElfTlsGetAddr,
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             "UdivI64" => Ok(Self::UdivI64),
75             "SdivI64" => Ok(Self::SdivI64),
76             "UremI64" => Ok(Self::UremI64),
77             "SremI64" => Ok(Self::SremI64),
78             "CeilF32" => Ok(Self::CeilF32),
79             "CeilF64" => Ok(Self::CeilF64),
80             "FloorF32" => Ok(Self::FloorF32),
81             "FloorF64" => Ok(Self::FloorF64),
82             "TruncF32" => Ok(Self::TruncF32),
83             "TruncF64" => Ok(Self::TruncF64),
84             "NearestF32" => Ok(Self::NearestF32),
85             "NearestF64" => Ok(Self::NearestF64),
86             "Memcpy" => Ok(Self::Memcpy),
87             "Memset" => Ok(Self::Memset),
88             "Memmove" => Ok(Self::Memmove),
89 
90             "ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr),
91             _ => Err(()),
92         }
93     }
94 }
95 
96 impl LibCall {
97     /// Get the well-known library call name to use as a replacement for an instruction with the
98     /// given opcode and controlling type variable.
99     ///
100     /// Returns `None` if no well-known library routine name exists for that instruction.
101     pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option<Self> {
102         Some(match ctrl_type {
103             types::I64 => match opcode {
104                 Opcode::Udiv => Self::UdivI64,
105                 Opcode::Sdiv => Self::SdivI64,
106                 Opcode::Urem => Self::UremI64,
107                 Opcode::Srem => Self::SremI64,
108                 _ => return None,
109             },
110             types::F32 => match opcode {
111                 Opcode::Ceil => Self::CeilF32,
112                 Opcode::Floor => Self::FloorF32,
113                 Opcode::Trunc => Self::TruncF32,
114                 Opcode::Nearest => Self::NearestF32,
115                 _ => return None,
116             },
117             types::F64 => match opcode {
118                 Opcode::Ceil => Self::CeilF64,
119                 Opcode::Floor => Self::FloorF64,
120                 Opcode::Trunc => Self::TruncF64,
121                 Opcode::Nearest => Self::NearestF64,
122                 _ => return None,
123             },
124             _ => return None,
125         })
126     }
127 }
128 
129 /// Get a function reference for `libcall` in `func`, following the signature
130 /// for `inst`.
131 ///
132 /// If there is an existing reference, use it, otherwise make a new one.
133 pub(crate) fn get_libcall_funcref(
134     libcall: LibCall,
135     call_conv: CallConv,
136     func: &mut Function,
137     inst: Inst,
138     isa: &dyn TargetIsa,
139 ) -> FuncRef {
140     find_funcref(libcall, func)
141         .unwrap_or_else(|| make_funcref_for_inst(libcall, call_conv, func, inst, isa))
142 }
143 
144 /// Get a function reference for the probestack function in `func`.
145 ///
146 /// If there is an existing reference, use it, otherwise make a new one.
147 pub fn get_probestack_funcref(
148     func: &mut Function,
149     reg_type: Type,
150     arg_reg: RegUnit,
151     isa: &dyn TargetIsa,
152 ) -> FuncRef {
153     find_funcref(LibCall::Probestack, func)
154         .unwrap_or_else(|| make_funcref_for_probestack(func, reg_type, arg_reg, isa))
155 }
156 
157 /// Get the existing function reference for `libcall` in `func` if it exists.
158 fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
159     // We're assuming that all libcall function decls are at the end.
160     // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless.
161     for (fref, func_data) in func.dfg.ext_funcs.iter().rev() {
162         match func_data.name {
163             ExternalName::LibCall(lc) => {
164                 if lc == libcall {
165                     return Some(fref);
166                 }
167             }
168             _ => break,
169         }
170     }
171     None
172 }
173 
174 /// Create a funcref for `LibCall::Probestack`.
175 fn make_funcref_for_probestack(
176     func: &mut Function,
177     reg_type: Type,
178     arg_reg: RegUnit,
179     isa: &dyn TargetIsa,
180 ) -> FuncRef {
181     let mut sig = Signature::new(CallConv::Probestack);
182     let rax = AbiParam::special_reg(reg_type, ArgumentPurpose::Normal, arg_reg);
183     sig.params.push(rax);
184     if !isa.flags().probestack_func_adjusts_sp() {
185         sig.returns.push(rax);
186     }
187     make_funcref(LibCall::Probestack, func, sig, isa)
188 }
189 
190 /// Create a funcref for `libcall` with a signature matching `inst`.
191 fn make_funcref_for_inst(
192     libcall: LibCall,
193     call_conv: CallConv,
194     func: &mut Function,
195     inst: Inst,
196     isa: &dyn TargetIsa,
197 ) -> FuncRef {
198     let mut sig = Signature::new(call_conv);
199     for &v in func.dfg.inst_args(inst) {
200         sig.params.push(AbiParam::new(func.dfg.value_type(v)));
201     }
202     for &v in func.dfg.inst_results(inst) {
203         sig.returns.push(AbiParam::new(func.dfg.value_type(v)));
204     }
205 
206     if call_conv.extends_baldrdash() {
207         // Adds the special VMContext parameter to the signature.
208         sig.params.push(AbiParam::special(
209             isa.pointer_type(),
210             ArgumentPurpose::VMContext,
211         ));
212     }
213 
214     make_funcref(libcall, func, sig, isa)
215 }
216 
217 /// Create a funcref for `libcall`.
218 fn make_funcref(
219     libcall: LibCall,
220     func: &mut Function,
221     sig: Signature,
222     isa: &dyn TargetIsa,
223 ) -> FuncRef {
224     let sigref = func.import_signature(sig);
225 
226     func.import_function(ExtFuncData {
227         name: ExternalName::LibCall(libcall),
228         signature: sigref,
229         colocated: isa.flags().use_colocated_libcalls(),
230     })
231 }
232 
233 #[cfg(test)]
234 mod tests {
235     use super::*;
236     use alloc::string::ToString;
237 
238     #[test]
239     fn display() {
240         assert_eq!(LibCall::CeilF32.to_string(), "CeilF32");
241         assert_eq!(LibCall::NearestF64.to_string(), "NearestF64");
242     }
243 
244     #[test]
245     fn parsing() {
246         assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32));
247     }
248 }
249