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