cranelift_codegen/ir/
libcall.rs1use 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#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
21#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
22pub enum LibCall {
23 Probestack,
26 CeilF32,
28 CeilF64,
30 FloorF32,
32 FloorF64,
34 TruncF32,
36 TruncF64,
38 NearestF32,
40 NearestF64,
42 FmaF32,
44 FmaF64,
46 Memcpy,
48 Memset,
50 Memmove,
52 Memcmp,
54
55 ElfTlsGetAddr,
57 ElfTlsGetOffset,
59 }
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 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 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
156pub fn get_probestack_funcref(func: &mut Function) -> Option<FuncRef> {
160 find_funcref(LibCall::Probestack, func)
161}
162
163fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
165 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}