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