Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/ir/libcall.rs
1693 views
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 {
66
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
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.
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.
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.
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]
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
}
233
234