1 //! Naming well-known routines in the runtime library.
2
3 use crate::{
4 ir::{AbiParam, ExternalName, FuncRef, Function, Signature, Type, types},
5 isa::CallConv,
6 };
7 use core::fmt;
8 use core::str::FromStr;
9 #[cfg(feature = "enable-serde")]
10 use 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))]
22 pub 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 /// trunc.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
65 impl fmt::Display for LibCall {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 fmt::Debug::fmt(self, f)
68 }
69 }
70
71 impl FromStr for LibCall {
72 type Err = ();
73
from_str(s: &str) -> Result<Self, Self::Err>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
101 impl LibCall {
102 /// Get a list of all known `LibCall`'s.
all_libcalls() -> &'static [LibCall]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].
signature(&self, call_conv: CallConv, pointer_type: Type) -> Signature128 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.
get_probestack_funcref(func: &mut Function) -> Option<FuncRef>189 pub 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.
find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef>194 fn 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)]
211 mod tests {
212 use super::*;
213 use alloc::string::ToString;
214
215 #[test]
display()216 fn display() {
217 assert_eq!(LibCall::CeilF32.to_string(), "CeilF32");
218 assert_eq!(LibCall::NearestF64.to_string(), "NearestF64");
219 }
220
221 #[test]
parsing()222 fn parsing() {
223 assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32));
224 }
225
226 #[test]
all_libcalls_to_from_string()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 }
233