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.
lookup(triple: Triple) -> Result<Builder>40 pub fn lookup(triple: Triple) -> Result<Builder> {
41 match triple.architecture {
42 Architecture::X86_64 => {
43 isa_builder!(x64, (feature = "x64"), triple)
44 }
45 Architecture::Aarch64 { .. } => {
46 isa_builder!(aarch64, (feature = "arm64"), triple)
47 }
48
49 _ => Err(format_err!(LookupError::Unsupported)),
50 }
51 }
52
53 impl error::Error for LookupError {}
54 impl Display for LookupError {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result55 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
56 match self {
57 LookupError::Unsupported => write!(f, "This target is not supported yet"),
58 LookupError::SupportDisabled => write!(f, "Support for this target was disabled"),
59 }
60 }
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`.
is_fastcall(&self) -> bool104 fn is_fastcall(&self) -> bool {
105 match &self {
106 CallingConvention::WindowsFastcall => true,
107 _ => false,
108 }
109 }
110
111 /// Returns true if the current calling convention is `SystemV`.
is_systemv(&self) -> bool112 fn is_systemv(&self) -> bool {
113 match &self {
114 CallingConvention::SystemV => true,
115 _ => false,
116 }
117 }
118
119 /// Returns true if the current calling convention is `AppleAarch64`.
is_apple_aarch64(&self) -> bool120 fn is_apple_aarch64(&self) -> bool {
121 match &self {
122 CallingConvention::AppleAarch64 => true,
123 _ => false,
124 }
125 }
126
127 /// Returns true if the current calling convention is `Default`.
is_default(&self) -> bool128 pub fn is_default(&self) -> bool {
129 match &self {
130 CallingConvention::Default => true,
131 _ => false,
132 }
133 }
134 }
135
136 impl From<CallingConvention> for CallConv {
from(value: CallingConvention) -> Self137 fn from(value: CallingConvention) -> Self {
138 match value {
139 CallingConvention::SystemV => Self::SystemV,
140 CallingConvention::AppleAarch64 => Self::AppleAarch64,
141 CallingConvention::Default => Self::Winch,
142 CallingConvention::WindowsFastcall => Self::WindowsFastcall,
143 }
144 }
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.
name(&self) -> &'static str151 fn name(&self) -> &'static str;
152
153 /// Get the target triple of the ISA.
triple(&self) -> &Triple154 fn triple(&self) -> &Triple;
155
156 /// Get the ISA-independent flags that were used to make this trait object.
flags(&self) -> &settings::Flags157 fn flags(&self) -> &settings::Flags;
158
159 /// Get the ISA-dependent flag values that were used to make this trait object.
isa_flags(&self) -> Vec<settings::Value>160 fn isa_flags(&self) -> Vec<settings::Value>;
161
162 /// Get a flag indicating whether branch protection is enabled.
is_branch_protection_enabled(&self) -> bool163 fn is_branch_protection_enabled(&self) -> bool {
164 false
165 }
166
167 /// Compile a function.
compile_function( &self, sig: &WasmFuncType, body: &FunctionBody, translation: &ModuleTranslation, types: &ModuleTypesBuilder, builtins: &mut BuiltinFunctions, validator: &mut FuncValidator<ValidatorResources>, tunables: &Tunables, ) -> Result<CompiledFunction>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.
default_call_conv(&self) -> CallConv180 fn default_call_conv(&self) -> CallConv {
181 CallConv::triple_default(&self.triple())
182 }
183
184 /// Derive Wasmtime's calling convention from the triple's default
185 /// calling convention.
wasmtime_call_conv(&self) -> CallingConvention186 fn wasmtime_call_conv(&self) -> CallingConvention {
187 match self.default_call_conv() {
188 CallConv::AppleAarch64 => CallingConvention::AppleAarch64,
189 CallConv::SystemV => CallingConvention::SystemV,
190 CallConv::WindowsFastcall => CallingConvention::WindowsFastcall,
191 cc => unimplemented!("calling convention: {:?}", cc),
192 }
193 }
194
195 /// Get the endianness of the underlying target triple.
endianness(&self) -> target_lexicon::Endianness196 fn endianness(&self) -> target_lexicon::Endianness {
197 self.triple().endianness().unwrap()
198 }
199
emit_unwind_info( &self, _result: &MachBufferFinalized<Final>, _kind: UnwindInfoKind, ) -> Result<Option<UnwindInfo>>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`.
create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry>207 fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
208 // By default, an ISA cannot create a System V CIE.
209 None
210 }
211
212 /// See `cranelift_codegen::isa::TargetIsa::text_section_builder`.
text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>213 fn text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>;
214
215 /// See `cranelift_codegen::isa::TargetIsa::function_alignment`.
function_alignment(&self) -> u32216 fn function_alignment(&self) -> u32;
217
218 /// Returns the pointer width of the ISA in bytes.
pointer_bytes(&self) -> u8219 fn pointer_bytes(&self) -> u8 {
220 let width = self.triple().pointer_width().unwrap();
221 width.bytes()
222 }
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.
page_size_align_log2(&self) -> u8228 fn page_size_align_log2(&self) -> u8;
229 }
230
231 impl Debug for &dyn TargetIsa {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result232 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
233 write!(
234 f,
235 "Target ISA {{ triple: {:?}, calling convention: {:?} }}",
236 self.triple(),
237 self.default_call_conv()
238 )
239 }
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 {
with_limits_per_class(int: u8, float: u8) -> Self260 fn with_limits_per_class(int: u8, float: u8) -> Self {
261 let int = RegClassEnv {
262 limit: int,
263 index: 0,
264 };
265
266 let float = RegClassEnv {
267 limit: float,
268 index: 0,
269 };
270
271 Self {
272 int,
273 float: Some(float),
274 }
275 }
276
with_absolute_limit(limit: u8) -> Self277 fn with_absolute_limit(limit: u8) -> Self {
278 let int = RegClassEnv { limit, index: 0 };
279
280 Self { int, float: None }
281 }
282 }
283
284 impl RegIndexEnv {
next_gpr(&mut self) -> Option<u8>285 fn next_gpr(&mut self) -> Option<u8> {
286 (self.int.index < self.int.limit)
287 .then(|| Self::increment(&mut self.int.index))
288 .flatten()
289 }
290
next_fpr(&mut self) -> Option<u8>291 fn next_fpr(&mut self) -> Option<u8> {
292 if let Some(f) = self.float.as_mut() {
293 (f.index < f.limit)
294 .then(|| Self::increment(&mut f.index))
295 .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 self.next_gpr()
300 }
301 }
302
increment(index: &mut u8) -> Option<u8>303 fn increment(index: &mut u8) -> Option<u8> {
304 let current = *index;
305 match index.checked_add(1) {
306 Some(next) => {
307 *index = next;
308 Some(current)
309 }
310 None => None,
311 }
312 }
313 }
314
315 #[cfg(test)]
316 mod tests {
317 use super::RegIndexEnv;
318 #[test]
test_get_next_reg_index()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]
test_reg_index_env_absolute_count()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 }
338