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