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