Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/winch/codegen/src/isa/mod.rs
1692 views
1
use crate::BuiltinFunctions;
2
use anyhow::{Result, anyhow};
3
use core::fmt::Formatter;
4
use cranelift_codegen::isa::unwind::{UnwindInfo, UnwindInfoKind};
5
use cranelift_codegen::isa::{CallConv, IsaBuilder};
6
use cranelift_codegen::settings;
7
use cranelift_codegen::{Final, MachBufferFinalized, TextSectionBuilder};
8
use std::{
9
error,
10
fmt::{self, Debug, Display},
11
};
12
use target_lexicon::{Architecture, Triple};
13
use wasmparser::{FuncValidator, FunctionBody, ValidatorResources};
14
use wasmtime_cranelift::CompiledFunction;
15
use wasmtime_environ::{ModuleTranslation, ModuleTypesBuilder, Tunables, WasmFuncType};
16
17
#[cfg(feature = "x64")]
18
pub(crate) mod x64;
19
20
#[cfg(feature = "arm64")]
21
pub(crate) mod aarch64;
22
23
pub(crate) mod reg;
24
25
macro_rules! isa_builder {
26
($name: ident, $cfg_terms: tt, $triple: ident) => {{
27
#[cfg $cfg_terms]
28
{
29
Ok($name::isa_builder($triple))
30
}
31
#[cfg(not $cfg_terms)]
32
{
33
Err(anyhow!(LookupError::SupportDisabled))
34
}
35
}};
36
}
37
38
pub type Builder = IsaBuilder<Result<Box<dyn TargetIsa>>>;
39
40
/// Look for an ISA builder for the given target triple.
41
pub fn lookup(triple: Triple) -> Result<Builder> {
42
match triple.architecture {
43
Architecture::X86_64 => {
44
isa_builder!(x64, (feature = "x64"), triple)
45
}
46
Architecture::Aarch64 { .. } => {
47
isa_builder!(aarch64, (feature = "arm64"), triple)
48
}
49
50
_ => Err(anyhow!(LookupError::Unsupported)),
51
}
52
}
53
54
impl error::Error for LookupError {}
55
impl Display for LookupError {
56
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
57
match self {
58
LookupError::Unsupported => write!(f, "This target is not supported yet"),
59
LookupError::SupportDisabled => write!(f, "Support for this target was disabled"),
60
}
61
}
62
}
63
64
#[derive(Debug)]
65
pub(crate) enum LookupError {
66
Unsupported,
67
// This directive covers the case in which the consumer
68
// enables the `all-arch` feature; in such case, this variant
69
// will never be used. This is most likely going to change
70
// in the future; this is one of the simplest options for now.
71
#[allow(dead_code, reason = "see comment")]
72
SupportDisabled,
73
}
74
75
/// Calling conventions supported by Winch. Winch supports a variation of
76
/// the calling conventions defined in this enum plus an internal default
77
/// calling convention.
78
///
79
/// This enum is a reduced subset of the calling conventions defined in
80
/// [cranelift_codegen::isa::CallConv]. Introducing this enum makes it easier
81
/// to enforce the invariant of all the calling conventions supported by Winch.
82
///
83
/// The main difference between the system calling conventions defined in
84
/// this enum and their native counterparts is how multiple returns are handled.
85
/// Given that Winch is not meant to be a standalone code generator, the code
86
/// it generates is tightly coupled to how Wasmtime expects multiple returns
87
/// to be handled: the first return in a register, dictated by the calling
88
/// convention and the rest, if any, via a return pointer.
89
#[derive(Copy, Clone, Debug)]
90
pub enum CallingConvention {
91
/// See [cranelift_codegen::isa::CallConv::SystemV]
92
SystemV,
93
/// See [cranelift_codegen::isa::CallConv::WindowsFastcall]
94
WindowsFastcall,
95
/// See [cranelift_codegen::isa::CallConv::AppleAarch64]
96
AppleAarch64,
97
/// The default calling convention for Winch. It largely follows SystemV
98
/// for parameter and result handling. This calling convention is part of
99
/// Winch's default ABI `crate::abi::ABI`.
100
Default,
101
}
102
103
impl CallingConvention {
104
/// Returns true if the current calling convention is `WindowsFastcall`.
105
fn is_fastcall(&self) -> bool {
106
match &self {
107
CallingConvention::WindowsFastcall => true,
108
_ => false,
109
}
110
}
111
112
/// Returns true if the current calling convention is `SystemV`.
113
fn is_systemv(&self) -> bool {
114
match &self {
115
CallingConvention::SystemV => true,
116
_ => false,
117
}
118
}
119
120
/// Returns true if the current calling convention is `AppleAarch64`.
121
fn is_apple_aarch64(&self) -> bool {
122
match &self {
123
CallingConvention::AppleAarch64 => true,
124
_ => false,
125
}
126
}
127
128
/// Returns true if the current calling convention is `Default`.
129
pub fn is_default(&self) -> bool {
130
match &self {
131
CallingConvention::Default => true,
132
_ => false,
133
}
134
}
135
}
136
137
impl From<CallingConvention> for CallConv {
138
fn from(value: CallingConvention) -> Self {
139
match value {
140
CallingConvention::SystemV => Self::SystemV,
141
CallingConvention::AppleAarch64 => Self::AppleAarch64,
142
CallingConvention::Default => Self::Winch,
143
CallingConvention::WindowsFastcall => Self::WindowsFastcall,
144
}
145
}
146
}
147
148
/// A trait representing commonalities between the supported
149
/// instruction set architectures.
150
pub trait TargetIsa: Send + Sync {
151
/// Get the name of the ISA.
152
fn name(&self) -> &'static str;
153
154
/// Get the target triple of the ISA.
155
fn triple(&self) -> &Triple;
156
157
/// Get the ISA-independent flags that were used to make this trait object.
158
fn flags(&self) -> &settings::Flags;
159
160
/// Get the ISA-dependent flag values that were used to make this trait object.
161
fn isa_flags(&self) -> Vec<settings::Value>;
162
163
/// Get a flag indicating whether branch protection is enabled.
164
fn is_branch_protection_enabled(&self) -> bool {
165
false
166
}
167
168
/// Compile a function.
169
fn compile_function(
170
&self,
171
sig: &WasmFuncType,
172
body: &FunctionBody,
173
translation: &ModuleTranslation,
174
types: &ModuleTypesBuilder,
175
builtins: &mut BuiltinFunctions,
176
validator: &mut FuncValidator<ValidatorResources>,
177
tunables: &Tunables,
178
) -> Result<CompiledFunction>;
179
180
/// Get the default calling convention of the underlying target triple.
181
fn default_call_conv(&self) -> CallConv {
182
CallConv::triple_default(&self.triple())
183
}
184
185
/// Derive Wasmtime's calling convention from the triple's default
186
/// calling convention.
187
fn wasmtime_call_conv(&self) -> CallingConvention {
188
match self.default_call_conv() {
189
CallConv::AppleAarch64 => CallingConvention::AppleAarch64,
190
CallConv::SystemV => CallingConvention::SystemV,
191
CallConv::WindowsFastcall => CallingConvention::WindowsFastcall,
192
cc => unimplemented!("calling convention: {:?}", cc),
193
}
194
}
195
196
/// Get the endianness of the underlying target triple.
197
fn endianness(&self) -> target_lexicon::Endianness {
198
self.triple().endianness().unwrap()
199
}
200
201
fn emit_unwind_info(
202
&self,
203
_result: &MachBufferFinalized<Final>,
204
_kind: UnwindInfoKind,
205
) -> Result<Option<UnwindInfo>>;
206
207
/// See `cranelift_codegen::isa::TargetIsa::create_systemv_cie`.
208
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
209
// By default, an ISA cannot create a System V CIE.
210
None
211
}
212
213
/// See `cranelift_codegen::isa::TargetIsa::text_section_builder`.
214
fn text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>;
215
216
/// See `cranelift_codegen::isa::TargetIsa::function_alignment`.
217
fn function_alignment(&self) -> u32;
218
219
/// Returns the pointer width of the ISA in bytes.
220
fn pointer_bytes(&self) -> u8 {
221
let width = self.triple().pointer_width().unwrap();
222
width.bytes()
223
}
224
225
/// The log2 of the target's page size and alignment.
226
///
227
/// Note that this may be an upper-bound that is larger than necessary for
228
/// some platforms since it may depend on runtime configuration.
229
fn page_size_align_log2(&self) -> u8;
230
}
231
232
impl Debug for &dyn TargetIsa {
233
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
234
write!(
235
f,
236
"Target ISA {{ triple: {:?}, calling convention: {:?} }}",
237
self.triple(),
238
self.default_call_conv()
239
)
240
}
241
}
242
243
/// Per-class register environment.
244
pub(crate) struct RegClassEnv {
245
/// Float register class limit.
246
limit: u8,
247
/// Float register class index.
248
index: u8,
249
}
250
251
/// Helper environment to track register assignment for Winch's default calling
252
/// convention.
253
pub(crate) struct RegIndexEnv {
254
/// Int register environment.
255
int: RegClassEnv,
256
/// Float register environment.
257
float: Option<RegClassEnv>,
258
}
259
260
impl RegIndexEnv {
261
fn with_limits_per_class(int: u8, float: u8) -> Self {
262
let int = RegClassEnv {
263
limit: int,
264
index: 0,
265
};
266
267
let float = RegClassEnv {
268
limit: float,
269
index: 0,
270
};
271
272
Self {
273
int,
274
float: Some(float),
275
}
276
}
277
278
fn with_absolute_limit(limit: u8) -> Self {
279
let int = RegClassEnv { limit, index: 0 };
280
281
Self { int, float: None }
282
}
283
}
284
285
impl RegIndexEnv {
286
fn next_gpr(&mut self) -> Option<u8> {
287
(self.int.index < self.int.limit)
288
.then(|| Self::increment(&mut self.int.index))
289
.flatten()
290
}
291
292
fn next_fpr(&mut self) -> Option<u8> {
293
if let Some(f) = self.float.as_mut() {
294
(f.index < f.limit)
295
.then(|| Self::increment(&mut f.index))
296
.flatten()
297
} else {
298
// If a single `RegClassEnv` is used, it means that the count is
299
// absolute, so we default to calling `next_gpr`.
300
self.next_gpr()
301
}
302
}
303
304
fn increment(index: &mut u8) -> Option<u8> {
305
let current = *index;
306
match index.checked_add(1) {
307
Some(next) => {
308
*index = next;
309
Some(current)
310
}
311
None => None,
312
}
313
}
314
}
315
316
#[cfg(test)]
317
mod tests {
318
use super::RegIndexEnv;
319
#[test]
320
fn test_get_next_reg_index() {
321
let mut index_env = RegIndexEnv::with_limits_per_class(3, 3);
322
assert_eq!(index_env.next_fpr(), Some(0));
323
assert_eq!(index_env.next_gpr(), Some(0));
324
assert_eq!(index_env.next_fpr(), Some(1));
325
assert_eq!(index_env.next_gpr(), Some(1));
326
assert_eq!(index_env.next_fpr(), Some(2));
327
assert_eq!(index_env.next_gpr(), Some(2));
328
}
329
330
#[test]
331
fn test_reg_index_env_absolute_count() {
332
let mut e = RegIndexEnv::with_absolute_limit(4);
333
assert!(e.next_gpr() == Some(0));
334
assert!(e.next_fpr() == Some(1));
335
assert!(e.next_gpr() == Some(2));
336
assert!(e.next_fpr() == Some(3));
337
}
338
}
339
340