1747ad3c4Slazypassion //! Naming well-known routines in the runtime library.
2747ad3c4Slazypassion 
3747ad3c4Slazypassion use crate::ir::{
4747ad3c4Slazypassion     types, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, Inst, Opcode,
5747ad3c4Slazypassion     Signature, Type,
6747ad3c4Slazypassion };
7747ad3c4Slazypassion use crate::isa::{CallConv, RegUnit, TargetIsa};
8747ad3c4Slazypassion use core::fmt;
9747ad3c4Slazypassion use core::str::FromStr;
109e884b44SArtur Jamro #[cfg(feature = "enable-serde")]
119e884b44SArtur Jamro use serde::{Deserialize, Serialize};
12747ad3c4Slazypassion 
13747ad3c4Slazypassion /// The name of a runtime library routine.
14747ad3c4Slazypassion ///
15747ad3c4Slazypassion /// Runtime library calls are generated for Cranelift IR instructions that don't have an equivalent
16747ad3c4Slazypassion /// ISA instruction or an easy macro expansion. A `LibCall` is used as a well-known name to refer to
17747ad3c4Slazypassion /// the runtime library routine. This way, Cranelift doesn't have to know about the naming
18747ad3c4Slazypassion /// convention in the embedding VM's runtime library.
19747ad3c4Slazypassion ///
20747ad3c4Slazypassion /// This list is likely to grow over time.
213293ca6bSPhilip Craig #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
229e884b44SArtur Jamro #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
23747ad3c4Slazypassion pub enum LibCall {
24747ad3c4Slazypassion     /// probe for stack overflow. These are emitted for functions which need
25*dd497c19SBenjamin Bouvier     /// when the `enable_probestack` setting is true.
26747ad3c4Slazypassion     Probestack,
27747ad3c4Slazypassion     /// ceil.f32
28747ad3c4Slazypassion     CeilF32,
29747ad3c4Slazypassion     /// ceil.f64
30747ad3c4Slazypassion     CeilF64,
31747ad3c4Slazypassion     /// floor.f32
32747ad3c4Slazypassion     FloorF32,
33747ad3c4Slazypassion     /// floor.f64
34747ad3c4Slazypassion     FloorF64,
35747ad3c4Slazypassion     /// trunc.f32
36747ad3c4Slazypassion     TruncF32,
37747ad3c4Slazypassion     /// frunc.f64
38747ad3c4Slazypassion     TruncF64,
39747ad3c4Slazypassion     /// nearest.f32
40747ad3c4Slazypassion     NearestF32,
41747ad3c4Slazypassion     /// nearest.f64
42747ad3c4Slazypassion     NearestF64,
43747ad3c4Slazypassion     /// libc.memcpy
44747ad3c4Slazypassion     Memcpy,
45747ad3c4Slazypassion     /// libc.memset
46747ad3c4Slazypassion     Memset,
47747ad3c4Slazypassion     /// libc.memmove
48747ad3c4Slazypassion     Memmove,
49747ad3c4Slazypassion }
50747ad3c4Slazypassion 
51747ad3c4Slazypassion impl fmt::Display for LibCall {
52747ad3c4Slazypassion     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53747ad3c4Slazypassion         fmt::Debug::fmt(self, f)
54747ad3c4Slazypassion     }
55747ad3c4Slazypassion }
56747ad3c4Slazypassion 
57747ad3c4Slazypassion impl FromStr for LibCall {
58747ad3c4Slazypassion     type Err = ();
59747ad3c4Slazypassion 
60747ad3c4Slazypassion     fn from_str(s: &str) -> Result<Self, Self::Err> {
61747ad3c4Slazypassion         match s {
629f506692SPeter Huene             "Probestack" => Ok(Self::Probestack),
639f506692SPeter Huene             "CeilF32" => Ok(Self::CeilF32),
649f506692SPeter Huene             "CeilF64" => Ok(Self::CeilF64),
659f506692SPeter Huene             "FloorF32" => Ok(Self::FloorF32),
669f506692SPeter Huene             "FloorF64" => Ok(Self::FloorF64),
679f506692SPeter Huene             "TruncF32" => Ok(Self::TruncF32),
689f506692SPeter Huene             "TruncF64" => Ok(Self::TruncF64),
699f506692SPeter Huene             "NearestF32" => Ok(Self::NearestF32),
709f506692SPeter Huene             "NearestF64" => Ok(Self::NearestF64),
719f506692SPeter Huene             "Memcpy" => Ok(Self::Memcpy),
729f506692SPeter Huene             "Memset" => Ok(Self::Memset),
739f506692SPeter Huene             "Memmove" => Ok(Self::Memmove),
74747ad3c4Slazypassion             _ => Err(()),
75747ad3c4Slazypassion         }
76747ad3c4Slazypassion     }
77747ad3c4Slazypassion }
78747ad3c4Slazypassion 
79747ad3c4Slazypassion impl LibCall {
80747ad3c4Slazypassion     /// Get the well-known library call name to use as a replacement for an instruction with the
81747ad3c4Slazypassion     /// given opcode and controlling type variable.
82747ad3c4Slazypassion     ///
83747ad3c4Slazypassion     /// Returns `None` if no well-known library routine name exists for that instruction.
84747ad3c4Slazypassion     pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option<Self> {
85747ad3c4Slazypassion         Some(match ctrl_type {
86747ad3c4Slazypassion             types::F32 => match opcode {
879f506692SPeter Huene                 Opcode::Ceil => Self::CeilF32,
889f506692SPeter Huene                 Opcode::Floor => Self::FloorF32,
899f506692SPeter Huene                 Opcode::Trunc => Self::TruncF32,
909f506692SPeter Huene                 Opcode::Nearest => Self::NearestF32,
91747ad3c4Slazypassion                 _ => return None,
92747ad3c4Slazypassion             },
93747ad3c4Slazypassion             types::F64 => match opcode {
949f506692SPeter Huene                 Opcode::Ceil => Self::CeilF64,
959f506692SPeter Huene                 Opcode::Floor => Self::FloorF64,
969f506692SPeter Huene                 Opcode::Trunc => Self::TruncF64,
979f506692SPeter Huene                 Opcode::Nearest => Self::NearestF64,
98747ad3c4Slazypassion                 _ => return None,
99747ad3c4Slazypassion             },
100747ad3c4Slazypassion             _ => return None,
101747ad3c4Slazypassion         })
102747ad3c4Slazypassion     }
103747ad3c4Slazypassion }
104747ad3c4Slazypassion 
105747ad3c4Slazypassion /// Get a function reference for `libcall` in `func`, following the signature
106747ad3c4Slazypassion /// for `inst`.
107747ad3c4Slazypassion ///
108747ad3c4Slazypassion /// If there is an existing reference, use it, otherwise make a new one.
109747ad3c4Slazypassion pub fn get_libcall_funcref(
110747ad3c4Slazypassion     libcall: LibCall,
111d8d36022SBenjamin Bouvier     call_conv: CallConv,
112747ad3c4Slazypassion     func: &mut Function,
113747ad3c4Slazypassion     inst: Inst,
114d7d48d5cSBenjamin Bouvier     isa: &dyn TargetIsa,
115747ad3c4Slazypassion ) -> FuncRef {
116d8d36022SBenjamin Bouvier     find_funcref(libcall, func)
117d8d36022SBenjamin Bouvier         .unwrap_or_else(|| make_funcref_for_inst(libcall, call_conv, func, inst, isa))
118747ad3c4Slazypassion }
119747ad3c4Slazypassion 
120747ad3c4Slazypassion /// Get a function reference for the probestack function in `func`.
121747ad3c4Slazypassion ///
122747ad3c4Slazypassion /// If there is an existing reference, use it, otherwise make a new one.
123747ad3c4Slazypassion pub fn get_probestack_funcref(
124747ad3c4Slazypassion     func: &mut Function,
125747ad3c4Slazypassion     reg_type: Type,
126747ad3c4Slazypassion     arg_reg: RegUnit,
127d7d48d5cSBenjamin Bouvier     isa: &dyn TargetIsa,
128747ad3c4Slazypassion ) -> FuncRef {
129747ad3c4Slazypassion     find_funcref(LibCall::Probestack, func)
130747ad3c4Slazypassion         .unwrap_or_else(|| make_funcref_for_probestack(func, reg_type, arg_reg, isa))
131747ad3c4Slazypassion }
132747ad3c4Slazypassion 
133747ad3c4Slazypassion /// Get the existing function reference for `libcall` in `func` if it exists.
134747ad3c4Slazypassion fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
135747ad3c4Slazypassion     // We're assuming that all libcall function decls are at the end.
136747ad3c4Slazypassion     // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless.
137747ad3c4Slazypassion     for (fref, func_data) in func.dfg.ext_funcs.iter().rev() {
138747ad3c4Slazypassion         match func_data.name {
139747ad3c4Slazypassion             ExternalName::LibCall(lc) => {
140747ad3c4Slazypassion                 if lc == libcall {
141747ad3c4Slazypassion                     return Some(fref);
142747ad3c4Slazypassion                 }
143747ad3c4Slazypassion             }
144747ad3c4Slazypassion             _ => break,
145747ad3c4Slazypassion         }
146747ad3c4Slazypassion     }
147747ad3c4Slazypassion     None
148747ad3c4Slazypassion }
149747ad3c4Slazypassion 
150747ad3c4Slazypassion /// Create a funcref for `LibCall::Probestack`.
151747ad3c4Slazypassion fn make_funcref_for_probestack(
152747ad3c4Slazypassion     func: &mut Function,
153747ad3c4Slazypassion     reg_type: Type,
154747ad3c4Slazypassion     arg_reg: RegUnit,
155d7d48d5cSBenjamin Bouvier     isa: &dyn TargetIsa,
156747ad3c4Slazypassion ) -> FuncRef {
157747ad3c4Slazypassion     let mut sig = Signature::new(CallConv::Probestack);
158747ad3c4Slazypassion     let rax = AbiParam::special_reg(reg_type, ArgumentPurpose::Normal, arg_reg);
159747ad3c4Slazypassion     sig.params.push(rax);
160747ad3c4Slazypassion     if !isa.flags().probestack_func_adjusts_sp() {
161747ad3c4Slazypassion         sig.returns.push(rax);
162747ad3c4Slazypassion     }
163747ad3c4Slazypassion     make_funcref(LibCall::Probestack, func, sig, isa)
164747ad3c4Slazypassion }
165747ad3c4Slazypassion 
166747ad3c4Slazypassion /// Create a funcref for `libcall` with a signature matching `inst`.
167747ad3c4Slazypassion fn make_funcref_for_inst(
168747ad3c4Slazypassion     libcall: LibCall,
169d8d36022SBenjamin Bouvier     call_conv: CallConv,
170747ad3c4Slazypassion     func: &mut Function,
171747ad3c4Slazypassion     inst: Inst,
172d7d48d5cSBenjamin Bouvier     isa: &dyn TargetIsa,
173747ad3c4Slazypassion ) -> FuncRef {
174d8d36022SBenjamin Bouvier     let mut sig = Signature::new(call_conv);
175747ad3c4Slazypassion     for &v in func.dfg.inst_args(inst) {
176747ad3c4Slazypassion         sig.params.push(AbiParam::new(func.dfg.value_type(v)));
177747ad3c4Slazypassion     }
178747ad3c4Slazypassion     for &v in func.dfg.inst_results(inst) {
179747ad3c4Slazypassion         sig.returns.push(AbiParam::new(func.dfg.value_type(v)));
180747ad3c4Slazypassion     }
181747ad3c4Slazypassion 
1822ee35b7eSBenjamin Bouvier     if call_conv.extends_baldrdash() {
183d8d36022SBenjamin Bouvier         // Adds the special VMContext parameter to the signature.
184d8d36022SBenjamin Bouvier         sig.params.push(AbiParam::special(
185d8d36022SBenjamin Bouvier             isa.pointer_type(),
186d8d36022SBenjamin Bouvier             ArgumentPurpose::VMContext,
187d8d36022SBenjamin Bouvier         ));
188d8d36022SBenjamin Bouvier     }
189d8d36022SBenjamin Bouvier 
190747ad3c4Slazypassion     make_funcref(libcall, func, sig, isa)
191747ad3c4Slazypassion }
192747ad3c4Slazypassion 
193747ad3c4Slazypassion /// Create a funcref for `libcall`.
194d7d48d5cSBenjamin Bouvier fn make_funcref(
195d7d48d5cSBenjamin Bouvier     libcall: LibCall,
196d7d48d5cSBenjamin Bouvier     func: &mut Function,
197d7d48d5cSBenjamin Bouvier     sig: Signature,
198d7d48d5cSBenjamin Bouvier     isa: &dyn TargetIsa,
199d7d48d5cSBenjamin Bouvier ) -> FuncRef {
200747ad3c4Slazypassion     let sigref = func.import_signature(sig);
201747ad3c4Slazypassion 
202747ad3c4Slazypassion     func.import_function(ExtFuncData {
203747ad3c4Slazypassion         name: ExternalName::LibCall(libcall),
204747ad3c4Slazypassion         signature: sigref,
205*dd497c19SBenjamin Bouvier         colocated: isa.flags().use_colocated_libcalls(),
206747ad3c4Slazypassion     })
207747ad3c4Slazypassion }
208747ad3c4Slazypassion 
209747ad3c4Slazypassion #[cfg(test)]
210747ad3c4Slazypassion mod tests {
211747ad3c4Slazypassion     use super::*;
21210e226f9Sbjorn3     use alloc::string::ToString;
213747ad3c4Slazypassion 
214747ad3c4Slazypassion     #[test]
215747ad3c4Slazypassion     fn display() {
216747ad3c4Slazypassion         assert_eq!(LibCall::CeilF32.to_string(), "CeilF32");
217747ad3c4Slazypassion         assert_eq!(LibCall::NearestF64.to_string(), "NearestF64");
218747ad3c4Slazypassion     }
219747ad3c4Slazypassion 
220747ad3c4Slazypassion     #[test]
221747ad3c4Slazypassion     fn parsing() {
222747ad3c4Slazypassion         assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32));
223747ad3c4Slazypassion     }
224747ad3c4Slazypassion }
225