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},
5    isa::CallConv,
6};
7use core::fmt;
8use core::str::FromStr;
9#[cfg(feature = "enable-serde")]
10use 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))]
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    // When adding a new variant make sure to add it to `all_libcalls` too.
60}
61
62impl fmt::Display for LibCall {
63    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        fmt::Debug::fmt(self, f)
65    }
66}
67
68impl 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
96impl 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.
159pub 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.
164fn 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)]
181mod 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}