1 //! Naming well-known routines in the runtime library. 2 3 use crate::ir::{types, AbiParam, ExternalName, FuncRef, Function, Opcode, Signature, Type}; 4 use crate::isa::CallConv; 5 use core::fmt; 6 use core::str::FromStr; 7 #[cfg(feature = "enable-serde")] 8 use serde::{Deserialize, Serialize}; 9 10 /// The name of a runtime library routine. 11 /// 12 /// Runtime library calls are generated for Cranelift IR instructions that don't have an equivalent 13 /// ISA instruction or an easy macro expansion. A `LibCall` is used as a well-known name to refer to 14 /// the runtime library routine. This way, Cranelift doesn't have to know about the naming 15 /// convention in the embedding VM's runtime library. 16 /// 17 /// This list is likely to grow over time. 18 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 19 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] 20 pub enum LibCall { 21 /// probe for stack overflow. These are emitted for functions which need 22 /// when the `enable_probestack` setting is true. 23 Probestack, 24 /// udiv.i64 25 UdivI64, 26 /// sdiv.i64 27 SdivI64, 28 /// urem.i64 29 UremI64, 30 /// srem.i64 31 SremI64, 32 /// ishl.i64 33 IshlI64, 34 /// ushr.i64 35 UshrI64, 36 /// sshr.i64 37 SshrI64, 38 /// ceil.f32 39 CeilF32, 40 /// ceil.f64 41 CeilF64, 42 /// floor.f32 43 FloorF32, 44 /// floor.f64 45 FloorF64, 46 /// trunc.f32 47 TruncF32, 48 /// frunc.f64 49 TruncF64, 50 /// nearest.f32 51 NearestF32, 52 /// nearest.f64 53 NearestF64, 54 /// fma.f32 55 FmaF32, 56 /// fma.f64 57 FmaF64, 58 /// libc.memcpy 59 Memcpy, 60 /// libc.memset 61 Memset, 62 /// libc.memmove 63 Memmove, 64 /// libc.memcmp 65 Memcmp, 66 67 /// Elf __tls_get_addr 68 ElfTlsGetAddr, 69 // When adding a new variant make sure to add it to `all_libcalls` too. 70 } 71 72 impl fmt::Display for LibCall { 73 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 74 fmt::Debug::fmt(self, f) 75 } 76 } 77 78 impl FromStr for LibCall { 79 type Err = (); 80 81 fn from_str(s: &str) -> Result<Self, Self::Err> { 82 match s { 83 "Probestack" => Ok(Self::Probestack), 84 "UdivI64" => Ok(Self::UdivI64), 85 "SdivI64" => Ok(Self::SdivI64), 86 "UremI64" => Ok(Self::UremI64), 87 "SremI64" => Ok(Self::SremI64), 88 "IshlI64" => Ok(Self::IshlI64), 89 "UshrI64" => Ok(Self::UshrI64), 90 "SshrI64" => Ok(Self::SshrI64), 91 "CeilF32" => Ok(Self::CeilF32), 92 "CeilF64" => Ok(Self::CeilF64), 93 "FloorF32" => Ok(Self::FloorF32), 94 "FloorF64" => Ok(Self::FloorF64), 95 "TruncF32" => Ok(Self::TruncF32), 96 "TruncF64" => Ok(Self::TruncF64), 97 "NearestF32" => Ok(Self::NearestF32), 98 "NearestF64" => Ok(Self::NearestF64), 99 "FmaF32" => Ok(Self::FmaF32), 100 "FmaF64" => Ok(Self::FmaF64), 101 "Memcpy" => Ok(Self::Memcpy), 102 "Memset" => Ok(Self::Memset), 103 "Memmove" => Ok(Self::Memmove), 104 "Memcmp" => Ok(Self::Memcmp), 105 106 "ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr), 107 _ => Err(()), 108 } 109 } 110 } 111 112 impl LibCall { 113 /// Get the well-known library call name to use as a replacement for an instruction with the 114 /// given opcode and controlling type variable. 115 /// 116 /// Returns `None` if no well-known library routine name exists for that instruction. 117 pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option<Self> { 118 Some(match ctrl_type { 119 types::I64 => match opcode { 120 Opcode::Udiv => Self::UdivI64, 121 Opcode::Sdiv => Self::SdivI64, 122 Opcode::Urem => Self::UremI64, 123 Opcode::Srem => Self::SremI64, 124 Opcode::Ishl => Self::IshlI64, 125 Opcode::Ushr => Self::UshrI64, 126 Opcode::Sshr => Self::SshrI64, 127 _ => return None, 128 }, 129 types::F32 => match opcode { 130 Opcode::Ceil => Self::CeilF32, 131 Opcode::Floor => Self::FloorF32, 132 Opcode::Trunc => Self::TruncF32, 133 Opcode::Nearest => Self::NearestF32, 134 Opcode::Fma => Self::FmaF32, 135 _ => return None, 136 }, 137 types::F64 => match opcode { 138 Opcode::Ceil => Self::CeilF64, 139 Opcode::Floor => Self::FloorF64, 140 Opcode::Trunc => Self::TruncF64, 141 Opcode::Nearest => Self::NearestF64, 142 Opcode::Fma => Self::FmaF64, 143 _ => return None, 144 }, 145 _ => return None, 146 }) 147 } 148 149 /// Get a list of all known `LibCall`'s. 150 pub fn all_libcalls() -> &'static [LibCall] { 151 use LibCall::*; 152 &[ 153 Probestack, 154 UdivI64, 155 SdivI64, 156 UremI64, 157 SremI64, 158 IshlI64, 159 UshrI64, 160 SshrI64, 161 CeilF32, 162 CeilF64, 163 FloorF32, 164 FloorF64, 165 TruncF32, 166 TruncF64, 167 NearestF32, 168 NearestF64, 169 FmaF32, 170 FmaF64, 171 Memcpy, 172 Memset, 173 Memmove, 174 Memcmp, 175 ElfTlsGetAddr, 176 ] 177 } 178 179 /// Get a [Signature] for the function targeted by this [LibCall]. 180 pub fn signature(&self, call_conv: CallConv) -> Signature { 181 use types::*; 182 let mut sig = Signature::new(call_conv); 183 184 match self { 185 LibCall::UdivI64 186 | LibCall::SdivI64 187 | LibCall::UremI64 188 | LibCall::SremI64 189 | LibCall::IshlI64 190 | LibCall::UshrI64 191 | LibCall::SshrI64 => { 192 sig.params.push(AbiParam::new(I64)); 193 sig.params.push(AbiParam::new(I64)); 194 sig.returns.push(AbiParam::new(I64)); 195 } 196 LibCall::CeilF32 | LibCall::FloorF32 | LibCall::TruncF32 | LibCall::NearestF32 => { 197 sig.params.push(AbiParam::new(F32)); 198 sig.returns.push(AbiParam::new(F32)); 199 } 200 LibCall::TruncF64 | LibCall::FloorF64 | LibCall::CeilF64 | LibCall::NearestF64 => { 201 sig.params.push(AbiParam::new(F64)); 202 sig.returns.push(AbiParam::new(F64)); 203 } 204 LibCall::FmaF32 | LibCall::FmaF64 => { 205 let ty = if *self == LibCall::FmaF32 { F32 } else { F64 }; 206 207 sig.params.push(AbiParam::new(ty)); 208 sig.params.push(AbiParam::new(ty)); 209 sig.params.push(AbiParam::new(ty)); 210 sig.returns.push(AbiParam::new(ty)); 211 } 212 LibCall::Probestack 213 | LibCall::Memcpy 214 | LibCall::Memset 215 | LibCall::Memmove 216 | LibCall::Memcmp 217 | LibCall::ElfTlsGetAddr => unimplemented!(), 218 } 219 220 sig 221 } 222 } 223 224 /// Get a function reference for the probestack function in `func`. 225 /// 226 /// If there is an existing reference, use it, otherwise make a new one. 227 pub fn get_probestack_funcref(func: &mut Function) -> Option<FuncRef> { 228 find_funcref(LibCall::Probestack, func) 229 } 230 231 /// Get the existing function reference for `libcall` in `func` if it exists. 232 fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> { 233 // We're assuming that all libcall function decls are at the end. 234 // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless. 235 for (fref, func_data) in func.dfg.ext_funcs.iter().rev() { 236 match func_data.name { 237 ExternalName::LibCall(lc) => { 238 if lc == libcall { 239 return Some(fref); 240 } 241 } 242 _ => break, 243 } 244 } 245 None 246 } 247 248 #[cfg(test)] 249 mod tests { 250 use super::*; 251 use alloc::string::ToString; 252 253 #[test] 254 fn display() { 255 assert_eq!(LibCall::CeilF32.to_string(), "CeilF32"); 256 assert_eq!(LibCall::NearestF64.to_string(), "NearestF64"); 257 } 258 259 #[test] 260 fn parsing() { 261 assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32)); 262 } 263 264 #[test] 265 fn all_libcalls_to_from_string() { 266 for &libcall in LibCall::all_libcalls() { 267 assert_eq!(libcall.to_string().parse(), Ok(libcall)); 268 } 269 } 270 } 271