1747ad3c4Slazypassion //! Naming well-known routines in the runtime library.
2747ad3c4Slazypassion 
38a9b1a90SBenjamin Bouvier use crate::{
4*90ac295eSAlex Crichton     ir::{AbiParam, ExternalName, FuncRef, Function, Signature, Type, types},
58a9b1a90SBenjamin Bouvier     isa::CallConv,
68a9b1a90SBenjamin Bouvier };
7747ad3c4Slazypassion use core::fmt;
8747ad3c4Slazypassion use core::str::FromStr;
99e884b44SArtur Jamro #[cfg(feature = "enable-serde")]
109ec02f9dSChristopher Serr use serde_derive::{Deserialize, Serialize};
11747ad3c4Slazypassion 
12747ad3c4Slazypassion /// The name of a runtime library routine.
13747ad3c4Slazypassion ///
14747ad3c4Slazypassion /// Runtime library calls are generated for Cranelift IR instructions that don't have an equivalent
15747ad3c4Slazypassion /// ISA instruction or an easy macro expansion. A `LibCall` is used as a well-known name to refer to
16747ad3c4Slazypassion /// the runtime library routine. This way, Cranelift doesn't have to know about the naming
17747ad3c4Slazypassion /// convention in the embedding VM's runtime library.
18747ad3c4Slazypassion ///
19747ad3c4Slazypassion /// This list is likely to grow over time.
203293ca6bSPhilip Craig #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
219e884b44SArtur Jamro #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
22747ad3c4Slazypassion pub enum LibCall {
23747ad3c4Slazypassion     /// probe for stack overflow. These are emitted for functions which need
24dd497c19SBenjamin Bouvier     /// when the `enable_probestack` setting is true.
25747ad3c4Slazypassion     Probestack,
26747ad3c4Slazypassion     /// ceil.f32
27747ad3c4Slazypassion     CeilF32,
28747ad3c4Slazypassion     /// ceil.f64
29747ad3c4Slazypassion     CeilF64,
30747ad3c4Slazypassion     /// floor.f32
31747ad3c4Slazypassion     FloorF32,
32747ad3c4Slazypassion     /// floor.f64
33747ad3c4Slazypassion     FloorF64,
34747ad3c4Slazypassion     /// trunc.f32
35747ad3c4Slazypassion     TruncF32,
36b30157e6SJohn Xu     /// trunc.f64
37747ad3c4Slazypassion     TruncF64,
38747ad3c4Slazypassion     /// nearest.f32
39747ad3c4Slazypassion     NearestF32,
40747ad3c4Slazypassion     /// nearest.f64
41747ad3c4Slazypassion     NearestF64,
42709716bbSAfonso Bordado     /// fma.f32
43709716bbSAfonso Bordado     FmaF32,
44709716bbSAfonso Bordado     /// fma.f64
45709716bbSAfonso Bordado     FmaF64,
46747ad3c4Slazypassion     /// libc.memcpy
47747ad3c4Slazypassion     Memcpy,
48747ad3c4Slazypassion     /// libc.memset
49747ad3c4Slazypassion     Memset,
50747ad3c4Slazypassion     /// libc.memmove
51747ad3c4Slazypassion     Memmove,
52c266f7f4SScott McMurray     /// libc.memcmp
53c266f7f4SScott McMurray     Memcmp,
540a1bb3baSbjorn3 
550a1bb3baSbjorn3     /// Elf __tls_get_addr
560a1bb3baSbjorn3     ElfTlsGetAddr,
5750fcab29SUlrich Weigand     /// Elf __tls_get_offset
5850fcab29SUlrich Weigand     ElfTlsGetOffset,
593dfbfb61SAlex Crichton 
603dfbfb61SAlex Crichton     /// The `pshufb` on x86 when SSSE3 isn't available.
613dfbfb61SAlex Crichton     X86Pshufb,
62eaa2c5b3Sbjorn3     // When adding a new variant make sure to add it to `all_libcalls` too.
63747ad3c4Slazypassion }
64747ad3c4Slazypassion 
65747ad3c4Slazypassion impl fmt::Display for LibCall {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result66747ad3c4Slazypassion     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67747ad3c4Slazypassion         fmt::Debug::fmt(self, f)
68747ad3c4Slazypassion     }
69747ad3c4Slazypassion }
70747ad3c4Slazypassion 
71747ad3c4Slazypassion impl FromStr for LibCall {
72747ad3c4Slazypassion     type Err = ();
73747ad3c4Slazypassion 
from_str(s: &str) -> Result<Self, Self::Err>74747ad3c4Slazypassion     fn from_str(s: &str) -> Result<Self, Self::Err> {
75747ad3c4Slazypassion         match s {
769f506692SPeter Huene             "Probestack" => Ok(Self::Probestack),
779f506692SPeter Huene             "CeilF32" => Ok(Self::CeilF32),
789f506692SPeter Huene             "CeilF64" => Ok(Self::CeilF64),
799f506692SPeter Huene             "FloorF32" => Ok(Self::FloorF32),
809f506692SPeter Huene             "FloorF64" => Ok(Self::FloorF64),
819f506692SPeter Huene             "TruncF32" => Ok(Self::TruncF32),
829f506692SPeter Huene             "TruncF64" => Ok(Self::TruncF64),
839f506692SPeter Huene             "NearestF32" => Ok(Self::NearestF32),
849f506692SPeter Huene             "NearestF64" => Ok(Self::NearestF64),
85709716bbSAfonso Bordado             "FmaF32" => Ok(Self::FmaF32),
86709716bbSAfonso Bordado             "FmaF64" => Ok(Self::FmaF64),
879f506692SPeter Huene             "Memcpy" => Ok(Self::Memcpy),
889f506692SPeter Huene             "Memset" => Ok(Self::Memset),
899f506692SPeter Huene             "Memmove" => Ok(Self::Memmove),
90c266f7f4SScott McMurray             "Memcmp" => Ok(Self::Memcmp),
910a1bb3baSbjorn3 
920a1bb3baSbjorn3             "ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr),
9350fcab29SUlrich Weigand             "ElfTlsGetOffset" => Ok(Self::ElfTlsGetOffset),
943dfbfb61SAlex Crichton 
953dfbfb61SAlex Crichton             "X86Pshufb" => Ok(Self::X86Pshufb),
96747ad3c4Slazypassion             _ => Err(()),
97747ad3c4Slazypassion         }
98747ad3c4Slazypassion     }
99747ad3c4Slazypassion }
100747ad3c4Slazypassion 
101747ad3c4Slazypassion impl LibCall {
102eaa2c5b3Sbjorn3     /// Get a list of all known `LibCall`'s.
all_libcalls() -> &'static [LibCall]103eaa2c5b3Sbjorn3     pub fn all_libcalls() -> &'static [LibCall] {
104eaa2c5b3Sbjorn3         use LibCall::*;
105eaa2c5b3Sbjorn3         &[
106eaa2c5b3Sbjorn3             Probestack,
107eaa2c5b3Sbjorn3             CeilF32,
108eaa2c5b3Sbjorn3             CeilF64,
109eaa2c5b3Sbjorn3             FloorF32,
110eaa2c5b3Sbjorn3             FloorF64,
111eaa2c5b3Sbjorn3             TruncF32,
112eaa2c5b3Sbjorn3             TruncF64,
113eaa2c5b3Sbjorn3             NearestF32,
114eaa2c5b3Sbjorn3             NearestF64,
115709716bbSAfonso Bordado             FmaF32,
116709716bbSAfonso Bordado             FmaF64,
117eaa2c5b3Sbjorn3             Memcpy,
118eaa2c5b3Sbjorn3             Memset,
119eaa2c5b3Sbjorn3             Memmove,
120c266f7f4SScott McMurray             Memcmp,
121eaa2c5b3Sbjorn3             ElfTlsGetAddr,
12250fcab29SUlrich Weigand             ElfTlsGetOffset,
1233dfbfb61SAlex Crichton             X86Pshufb,
124eaa2c5b3Sbjorn3         ]
125eaa2c5b3Sbjorn3     }
126709716bbSAfonso Bordado 
127709716bbSAfonso Bordado     /// Get a [Signature] for the function targeted by this [LibCall].
signature(&self, call_conv: CallConv, pointer_type: Type) -> Signature1280478ead3Sbjorn3     pub fn signature(&self, call_conv: CallConv, pointer_type: Type) -> Signature {
129709716bbSAfonso Bordado         use types::*;
130709716bbSAfonso Bordado         let mut sig = Signature::new(call_conv);
131709716bbSAfonso Bordado 
132709716bbSAfonso Bordado         match self {
133709716bbSAfonso Bordado             LibCall::CeilF32 | LibCall::FloorF32 | LibCall::TruncF32 | LibCall::NearestF32 => {
134709716bbSAfonso Bordado                 sig.params.push(AbiParam::new(F32));
135709716bbSAfonso Bordado                 sig.returns.push(AbiParam::new(F32));
136709716bbSAfonso Bordado             }
137709716bbSAfonso Bordado             LibCall::TruncF64 | LibCall::FloorF64 | LibCall::CeilF64 | LibCall::NearestF64 => {
138709716bbSAfonso Bordado                 sig.params.push(AbiParam::new(F64));
139709716bbSAfonso Bordado                 sig.returns.push(AbiParam::new(F64));
140709716bbSAfonso Bordado             }
141709716bbSAfonso Bordado             LibCall::FmaF32 | LibCall::FmaF64 => {
142709716bbSAfonso Bordado                 let ty = if *self == LibCall::FmaF32 { F32 } else { F64 };
143709716bbSAfonso Bordado 
144709716bbSAfonso Bordado                 sig.params.push(AbiParam::new(ty));
145709716bbSAfonso Bordado                 sig.params.push(AbiParam::new(ty));
146709716bbSAfonso Bordado                 sig.params.push(AbiParam::new(ty));
147709716bbSAfonso Bordado                 sig.returns.push(AbiParam::new(ty));
148709716bbSAfonso Bordado             }
1490478ead3Sbjorn3             LibCall::Memcpy | LibCall::Memmove => {
1500478ead3Sbjorn3                 // void* memcpy(void *dest, const void *src, size_t count);
1510478ead3Sbjorn3                 // void* memmove(void* dest, const void* src, size_t count);
1520478ead3Sbjorn3                 sig.params.push(AbiParam::new(pointer_type));
1530478ead3Sbjorn3                 sig.params.push(AbiParam::new(pointer_type));
1540478ead3Sbjorn3                 sig.params.push(AbiParam::new(pointer_type));
1550478ead3Sbjorn3                 sig.returns.push(AbiParam::new(pointer_type));
1560478ead3Sbjorn3             }
1570478ead3Sbjorn3             LibCall::Memset => {
1580478ead3Sbjorn3                 // void *memset(void *dest, int ch, size_t count);
1590478ead3Sbjorn3                 sig.params.push(AbiParam::new(pointer_type));
1600478ead3Sbjorn3                 sig.params.push(AbiParam::new(I32));
1610478ead3Sbjorn3                 sig.params.push(AbiParam::new(pointer_type));
1620478ead3Sbjorn3                 sig.returns.push(AbiParam::new(pointer_type));
1630478ead3Sbjorn3             }
1640478ead3Sbjorn3             LibCall::Memcmp => {
1650478ead3Sbjorn3                 // void* memcpy(void *dest, const void *src, size_t count);
1660478ead3Sbjorn3                 sig.params.push(AbiParam::new(pointer_type));
1670478ead3Sbjorn3                 sig.params.push(AbiParam::new(pointer_type));
1680478ead3Sbjorn3                 sig.params.push(AbiParam::new(pointer_type));
1690478ead3Sbjorn3                 sig.returns.push(AbiParam::new(I32))
1700478ead3Sbjorn3             }
1710478ead3Sbjorn3 
1720478ead3Sbjorn3             LibCall::Probestack | LibCall::ElfTlsGetAddr | LibCall::ElfTlsGetOffset => {
1730478ead3Sbjorn3                 unimplemented!()
1740478ead3Sbjorn3             }
1753dfbfb61SAlex Crichton             LibCall::X86Pshufb => {
1763dfbfb61SAlex Crichton                 sig.params.push(AbiParam::new(I8X16));
1773dfbfb61SAlex Crichton                 sig.params.push(AbiParam::new(I8X16));
1783dfbfb61SAlex Crichton                 sig.returns.push(AbiParam::new(I8X16));
1793dfbfb61SAlex Crichton             }
180709716bbSAfonso Bordado         }
181709716bbSAfonso Bordado 
182709716bbSAfonso Bordado         sig
183709716bbSAfonso Bordado     }
184747ad3c4Slazypassion }
185747ad3c4Slazypassion 
186747ad3c4Slazypassion /// Get a function reference for the probestack function in `func`.
187747ad3c4Slazypassion ///
188747ad3c4Slazypassion /// If there is an existing reference, use it, otherwise make a new one.
get_probestack_funcref(func: &mut Function) -> Option<FuncRef>18943a86f14SBenjamin Bouvier pub fn get_probestack_funcref(func: &mut Function) -> Option<FuncRef> {
190747ad3c4Slazypassion     find_funcref(LibCall::Probestack, func)
191747ad3c4Slazypassion }
192747ad3c4Slazypassion 
193747ad3c4Slazypassion /// Get the existing function reference for `libcall` in `func` if it exists.
find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef>194747ad3c4Slazypassion fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
195747ad3c4Slazypassion     // We're assuming that all libcall function decls are at the end.
196747ad3c4Slazypassion     // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless.
197747ad3c4Slazypassion     for (fref, func_data) in func.dfg.ext_funcs.iter().rev() {
198747ad3c4Slazypassion         match func_data.name {
199747ad3c4Slazypassion             ExternalName::LibCall(lc) => {
200747ad3c4Slazypassion                 if lc == libcall {
201747ad3c4Slazypassion                     return Some(fref);
202747ad3c4Slazypassion                 }
203747ad3c4Slazypassion             }
204747ad3c4Slazypassion             _ => break,
205747ad3c4Slazypassion         }
206747ad3c4Slazypassion     }
207747ad3c4Slazypassion     None
208747ad3c4Slazypassion }
209747ad3c4Slazypassion 
210747ad3c4Slazypassion #[cfg(test)]
211747ad3c4Slazypassion mod tests {
212747ad3c4Slazypassion     use super::*;
21310e226f9Sbjorn3     use alloc::string::ToString;
214747ad3c4Slazypassion 
215747ad3c4Slazypassion     #[test]
display()216747ad3c4Slazypassion     fn display() {
217747ad3c4Slazypassion         assert_eq!(LibCall::CeilF32.to_string(), "CeilF32");
218747ad3c4Slazypassion         assert_eq!(LibCall::NearestF64.to_string(), "NearestF64");
219747ad3c4Slazypassion     }
220747ad3c4Slazypassion 
221747ad3c4Slazypassion     #[test]
parsing()222747ad3c4Slazypassion     fn parsing() {
223747ad3c4Slazypassion         assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32));
224747ad3c4Slazypassion     }
225c266f7f4SScott McMurray 
226c266f7f4SScott McMurray     #[test]
all_libcalls_to_from_string()227c266f7f4SScott McMurray     fn all_libcalls_to_from_string() {
228c266f7f4SScott McMurray         for &libcall in LibCall::all_libcalls() {
229c266f7f4SScott McMurray             assert_eq!(libcall.to_string().parse(), Ok(libcall));
230c266f7f4SScott McMurray         }
231c266f7f4SScott McMurray     }
232747ad3c4Slazypassion }
233