cranelift_codegen/ir/
libcall.rs

1//! Naming well-known routines in the runtime library.
2
3use crate::{
4    ir::{types, AbiParam, ExternalName, FuncRef, Function, Signature, Type},
5    isa::CallConv,
6};
7use core::fmt;
8use core::str::FromStr;
9#[cfg(feature = "enable-serde")]
10use 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))]
22pub 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
65impl fmt::Display for LibCall {
66    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67        fmt::Debug::fmt(self, f)
68    }
69}
70
71impl 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
101impl 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.
189pub 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.
194fn 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)]
211mod 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}