1 //! Naming well-known routines in the runtime library. 2 3 use crate::ir::{ 4 types, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, Inst, 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 /// ceil.f32 36 CeilF32, 37 /// ceil.f64 38 CeilF64, 39 /// floor.f32 40 FloorF32, 41 /// floor.f64 42 FloorF64, 43 /// trunc.f32 44 TruncF32, 45 /// frunc.f64 46 TruncF64, 47 /// nearest.f32 48 NearestF32, 49 /// nearest.f64 50 NearestF64, 51 /// libc.memcpy 52 Memcpy, 53 /// libc.memset 54 Memset, 55 /// libc.memmove 56 Memmove, 57 58 /// Elf __tls_get_addr 59 ElfTlsGetAddr, 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 "UdivI64" => Ok(Self::UdivI64), 75 "SdivI64" => Ok(Self::SdivI64), 76 "UremI64" => Ok(Self::UremI64), 77 "SremI64" => Ok(Self::SremI64), 78 "CeilF32" => Ok(Self::CeilF32), 79 "CeilF64" => Ok(Self::CeilF64), 80 "FloorF32" => Ok(Self::FloorF32), 81 "FloorF64" => Ok(Self::FloorF64), 82 "TruncF32" => Ok(Self::TruncF32), 83 "TruncF64" => Ok(Self::TruncF64), 84 "NearestF32" => Ok(Self::NearestF32), 85 "NearestF64" => Ok(Self::NearestF64), 86 "Memcpy" => Ok(Self::Memcpy), 87 "Memset" => Ok(Self::Memset), 88 "Memmove" => Ok(Self::Memmove), 89 90 "ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr), 91 _ => Err(()), 92 } 93 } 94 } 95 96 impl LibCall { 97 /// Get the well-known library call name to use as a replacement for an instruction with the 98 /// given opcode and controlling type variable. 99 /// 100 /// Returns `None` if no well-known library routine name exists for that instruction. 101 pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option<Self> { 102 Some(match ctrl_type { 103 types::I64 => match opcode { 104 Opcode::Udiv => Self::UdivI64, 105 Opcode::Sdiv => Self::SdivI64, 106 Opcode::Urem => Self::UremI64, 107 Opcode::Srem => Self::SremI64, 108 _ => return None, 109 }, 110 types::F32 => match opcode { 111 Opcode::Ceil => Self::CeilF32, 112 Opcode::Floor => Self::FloorF32, 113 Opcode::Trunc => Self::TruncF32, 114 Opcode::Nearest => Self::NearestF32, 115 _ => return None, 116 }, 117 types::F64 => match opcode { 118 Opcode::Ceil => Self::CeilF64, 119 Opcode::Floor => Self::FloorF64, 120 Opcode::Trunc => Self::TruncF64, 121 Opcode::Nearest => Self::NearestF64, 122 _ => return None, 123 }, 124 _ => return None, 125 }) 126 } 127 } 128 129 /// Get a function reference for `libcall` in `func`, following the signature 130 /// for `inst`. 131 /// 132 /// If there is an existing reference, use it, otherwise make a new one. 133 pub(crate) fn get_libcall_funcref( 134 libcall: LibCall, 135 call_conv: CallConv, 136 func: &mut Function, 137 inst: Inst, 138 isa: &dyn TargetIsa, 139 ) -> FuncRef { 140 find_funcref(libcall, func) 141 .unwrap_or_else(|| make_funcref_for_inst(libcall, call_conv, func, inst, isa)) 142 } 143 144 /// Get a function reference for the probestack function in `func`. 145 /// 146 /// If there is an existing reference, use it, otherwise make a new one. 147 pub fn get_probestack_funcref( 148 func: &mut Function, 149 reg_type: Type, 150 arg_reg: RegUnit, 151 isa: &dyn TargetIsa, 152 ) -> FuncRef { 153 find_funcref(LibCall::Probestack, func) 154 .unwrap_or_else(|| make_funcref_for_probestack(func, reg_type, arg_reg, isa)) 155 } 156 157 /// Get the existing function reference for `libcall` in `func` if it exists. 158 fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> { 159 // We're assuming that all libcall function decls are at the end. 160 // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless. 161 for (fref, func_data) in func.dfg.ext_funcs.iter().rev() { 162 match func_data.name { 163 ExternalName::LibCall(lc) => { 164 if lc == libcall { 165 return Some(fref); 166 } 167 } 168 _ => break, 169 } 170 } 171 None 172 } 173 174 /// Create a funcref for `LibCall::Probestack`. 175 fn make_funcref_for_probestack( 176 func: &mut Function, 177 reg_type: Type, 178 arg_reg: RegUnit, 179 isa: &dyn TargetIsa, 180 ) -> FuncRef { 181 let mut sig = Signature::new(CallConv::Probestack); 182 let rax = AbiParam::special_reg(reg_type, ArgumentPurpose::Normal, arg_reg); 183 sig.params.push(rax); 184 if !isa.flags().probestack_func_adjusts_sp() { 185 sig.returns.push(rax); 186 } 187 make_funcref(LibCall::Probestack, func, sig, isa) 188 } 189 190 /// Create a funcref for `libcall` with a signature matching `inst`. 191 fn make_funcref_for_inst( 192 libcall: LibCall, 193 call_conv: CallConv, 194 func: &mut Function, 195 inst: Inst, 196 isa: &dyn TargetIsa, 197 ) -> FuncRef { 198 let mut sig = Signature::new(call_conv); 199 for &v in func.dfg.inst_args(inst) { 200 sig.params.push(AbiParam::new(func.dfg.value_type(v))); 201 } 202 for &v in func.dfg.inst_results(inst) { 203 sig.returns.push(AbiParam::new(func.dfg.value_type(v))); 204 } 205 206 if call_conv.extends_baldrdash() { 207 // Adds the special VMContext parameter to the signature. 208 sig.params.push(AbiParam::special( 209 isa.pointer_type(), 210 ArgumentPurpose::VMContext, 211 )); 212 } 213 214 make_funcref(libcall, func, sig, isa) 215 } 216 217 /// Create a funcref for `libcall`. 218 fn make_funcref( 219 libcall: LibCall, 220 func: &mut Function, 221 sig: Signature, 222 isa: &dyn TargetIsa, 223 ) -> FuncRef { 224 let sigref = func.import_signature(sig); 225 226 func.import_function(ExtFuncData { 227 name: ExternalName::LibCall(libcall), 228 signature: sigref, 229 colocated: isa.flags().use_colocated_libcalls(), 230 }) 231 } 232 233 #[cfg(test)] 234 mod tests { 235 use super::*; 236 use alloc::string::ToString; 237 238 #[test] 239 fn display() { 240 assert_eq!(LibCall::CeilF32.to_string(), "CeilF32"); 241 assert_eq!(LibCall::NearestF64.to_string(), "NearestF64"); 242 } 243 244 #[test] 245 fn parsing() { 246 assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32)); 247 } 248 } 249