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