1use crate::BuiltinFunctions;
2use anyhow::{anyhow, Result};
3use core::fmt::Formatter;
4use cranelift_codegen::isa::unwind::{UnwindInfo, UnwindInfoKind};
5use cranelift_codegen::isa::{CallConv, IsaBuilder};
6use cranelift_codegen::settings;
7use cranelift_codegen::{Final, MachBufferFinalized, TextSectionBuilder};
8use std::{
9 error,
10 fmt::{self, Debug, Display},
11};
12use target_lexicon::{Architecture, Triple};
13use wasmparser::{FuncValidator, FunctionBody, ValidatorResources};
14use wasmtime_cranelift::CompiledFunction;
15use wasmtime_environ::{ModuleTranslation, ModuleTypesBuilder, Tunables, WasmFuncType};
16
17#[cfg(feature = "x64")]
18pub(crate) mod x64;
19
20#[cfg(feature = "arm64")]
21pub(crate) mod aarch64;
22
23pub(crate) mod reg;
24
25macro_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
38pub type Builder = IsaBuilder<Result<Box<dyn TargetIsa>>>;
39
40pub 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
54impl error::Error for LookupError {}
55impl 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)]
65pub(crate) enum LookupError {
66 Unsupported,
67 #[allow(dead_code)]
72 SupportDisabled,
73}
74
75#[derive(Copy, Clone, Debug)]
90pub enum CallingConvention {
91 SystemV,
93 WindowsFastcall,
95 AppleAarch64,
97 Default,
101}
102
103impl CallingConvention {
104 fn is_fastcall(&self) -> bool {
106 match &self {
107 CallingConvention::WindowsFastcall => true,
108 _ => false,
109 }
110 }
111
112 fn is_systemv(&self) -> bool {
114 match &self {
115 CallingConvention::SystemV => true,
116 _ => false,
117 }
118 }
119
120 fn is_apple_aarch64(&self) -> bool {
122 match &self {
123 CallingConvention::AppleAarch64 => true,
124 _ => false,
125 }
126 }
127
128 pub fn is_default(&self) -> bool {
130 match &self {
131 CallingConvention::Default => true,
132 _ => false,
133 }
134 }
135}
136
137impl 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
148pub trait TargetIsa: Send + Sync {
151 fn name(&self) -> &'static str;
153
154 fn triple(&self) -> &Triple;
156
157 fn flags(&self) -> &settings::Flags;
159
160 fn isa_flags(&self) -> Vec<settings::Value>;
162
163 fn is_branch_protection_enabled(&self) -> bool {
165 false
166 }
167
168 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 fn default_call_conv(&self) -> CallConv {
182 CallConv::triple_default(&self.triple())
183 }
184
185 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 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 fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
209 None
211 }
212
213 fn text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>;
215
216 fn function_alignment(&self) -> u32;
218
219 fn pointer_bytes(&self) -> u8 {
221 let width = self.triple().pointer_width().unwrap();
222 width.bytes()
223 }
224
225 fn page_size_align_log2(&self) -> u8;
230}
231
232impl 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
243pub(crate) struct RegClassEnv {
245 limit: u8,
247 index: u8,
249}
250
251pub(crate) struct RegIndexEnv {
254 int: RegClassEnv,
256 float: Option<RegClassEnv>,
258}
259
260impl 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
285impl 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 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)]
317mod 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}