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