1 //! Naming well-known routines in the runtime library. 2 3 use crate::{ 4 ir::{types, AbiParam, ExternalName, FuncRef, Function, Signature}, 5 isa::CallConv, 6 }; 7 use core::fmt; 8 use core::str::FromStr; 9 #[cfg(feature = "enable-serde")] 10 use serde::{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 // When adding a new variant make sure to add it to `all_libcalls` too. 60 } 61 62 impl fmt::Display for LibCall { 63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 64 fmt::Debug::fmt(self, f) 65 } 66 } 67 68 impl FromStr for LibCall { 69 type Err = (); 70 71 fn from_str(s: &str) -> Result<Self, Self::Err> { 72 match s { 73 "Probestack" => Ok(Self::Probestack), 74 "CeilF32" => Ok(Self::CeilF32), 75 "CeilF64" => Ok(Self::CeilF64), 76 "FloorF32" => Ok(Self::FloorF32), 77 "FloorF64" => Ok(Self::FloorF64), 78 "TruncF32" => Ok(Self::TruncF32), 79 "TruncF64" => Ok(Self::TruncF64), 80 "NearestF32" => Ok(Self::NearestF32), 81 "NearestF64" => Ok(Self::NearestF64), 82 "FmaF32" => Ok(Self::FmaF32), 83 "FmaF64" => Ok(Self::FmaF64), 84 "Memcpy" => Ok(Self::Memcpy), 85 "Memset" => Ok(Self::Memset), 86 "Memmove" => Ok(Self::Memmove), 87 "Memcmp" => Ok(Self::Memcmp), 88 89 "ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr), 90 "ElfTlsGetOffset" => Ok(Self::ElfTlsGetOffset), 91 _ => Err(()), 92 } 93 } 94 } 95 96 impl LibCall { 97 /// Get a list of all known `LibCall`'s. 98 pub fn all_libcalls() -> &'static [LibCall] { 99 use LibCall::*; 100 &[ 101 Probestack, 102 CeilF32, 103 CeilF64, 104 FloorF32, 105 FloorF64, 106 TruncF32, 107 TruncF64, 108 NearestF32, 109 NearestF64, 110 FmaF32, 111 FmaF64, 112 Memcpy, 113 Memset, 114 Memmove, 115 Memcmp, 116 ElfTlsGetAddr, 117 ElfTlsGetOffset, 118 ] 119 } 120 121 /// Get a [Signature] for the function targeted by this [LibCall]. 122 pub fn signature(&self, call_conv: CallConv) -> Signature { 123 use types::*; 124 let mut sig = Signature::new(call_conv); 125 126 match self { 127 LibCall::CeilF32 | LibCall::FloorF32 | LibCall::TruncF32 | LibCall::NearestF32 => { 128 sig.params.push(AbiParam::new(F32)); 129 sig.returns.push(AbiParam::new(F32)); 130 } 131 LibCall::TruncF64 | LibCall::FloorF64 | LibCall::CeilF64 | LibCall::NearestF64 => { 132 sig.params.push(AbiParam::new(F64)); 133 sig.returns.push(AbiParam::new(F64)); 134 } 135 LibCall::FmaF32 | LibCall::FmaF64 => { 136 let ty = if *self == LibCall::FmaF32 { F32 } else { F64 }; 137 138 sig.params.push(AbiParam::new(ty)); 139 sig.params.push(AbiParam::new(ty)); 140 sig.params.push(AbiParam::new(ty)); 141 sig.returns.push(AbiParam::new(ty)); 142 } 143 LibCall::Probestack 144 | LibCall::Memcpy 145 | LibCall::Memset 146 | LibCall::Memmove 147 | LibCall::Memcmp 148 | LibCall::ElfTlsGetAddr 149 | LibCall::ElfTlsGetOffset => unimplemented!(), 150 } 151 152 sig 153 } 154 } 155 156 /// Get a function reference for the probestack function in `func`. 157 /// 158 /// If there is an existing reference, use it, otherwise make a new one. 159 pub fn get_probestack_funcref(func: &mut Function) -> Option<FuncRef> { 160 find_funcref(LibCall::Probestack, func) 161 } 162 163 /// Get the existing function reference for `libcall` in `func` if it exists. 164 fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> { 165 // We're assuming that all libcall function decls are at the end. 166 // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless. 167 for (fref, func_data) in func.dfg.ext_funcs.iter().rev() { 168 match func_data.name { 169 ExternalName::LibCall(lc) => { 170 if lc == libcall { 171 return Some(fref); 172 } 173 } 174 _ => break, 175 } 176 } 177 None 178 } 179 180 #[cfg(test)] 181 mod tests { 182 use super::*; 183 use alloc::string::ToString; 184 185 #[test] 186 fn display() { 187 assert_eq!(LibCall::CeilF32.to_string(), "CeilF32"); 188 assert_eq!(LibCall::NearestF64.to_string(), "NearestF64"); 189 } 190 191 #[test] 192 fn parsing() { 193 assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32)); 194 } 195 196 #[test] 197 fn all_libcalls_to_from_string() { 198 for &libcall in LibCall::all_libcalls() { 199 assert_eq!(libcall.to_string().parse(), Ok(libcall)); 200 } 201 } 202 } 203