1 use crate::BuiltinFunctions; 2 use anyhow::{anyhow, Result}; 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)] 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 /// A trait representing commonalities between the supported 138 /// instruction set architectures. 139 pub trait TargetIsa: Send + Sync { 140 /// Get the name of the ISA. 141 fn name(&self) -> &'static str; 142 143 /// Get the target triple of the ISA. 144 fn triple(&self) -> &Triple; 145 146 /// Get the ISA-independent flags that were used to make this trait object. 147 fn flags(&self) -> &settings::Flags; 148 149 /// Get the ISA-dependent flag values that were used to make this trait object. 150 fn isa_flags(&self) -> Vec<settings::Value>; 151 152 /// Get a flag indicating whether branch protection is enabled. 153 fn is_branch_protection_enabled(&self) -> bool { 154 false 155 } 156 157 /// Compile a function. 158 fn compile_function( 159 &self, 160 sig: &WasmFuncType, 161 body: &FunctionBody, 162 translation: &ModuleTranslation, 163 types: &ModuleTypesBuilder, 164 builtins: &mut BuiltinFunctions, 165 validator: &mut FuncValidator<ValidatorResources>, 166 tunables: &Tunables, 167 ) -> Result<CompiledFunction>; 168 169 /// Get the default calling convention of the underlying target triple. 170 fn default_call_conv(&self) -> CallConv { 171 CallConv::triple_default(&self.triple()) 172 } 173 174 /// Derive Wasmtime's calling convention from the triple's default 175 /// calling convention. 176 fn wasmtime_call_conv(&self) -> CallingConvention { 177 match self.default_call_conv() { 178 CallConv::AppleAarch64 => CallingConvention::AppleAarch64, 179 CallConv::SystemV => CallingConvention::SystemV, 180 CallConv::WindowsFastcall => CallingConvention::WindowsFastcall, 181 cc => unimplemented!("calling convention: {:?}", cc), 182 } 183 } 184 185 /// Get the endianness of the underlying target triple. 186 fn endianness(&self) -> target_lexicon::Endianness { 187 self.triple().endianness().unwrap() 188 } 189 190 fn emit_unwind_info( 191 &self, 192 _result: &MachBufferFinalized<Final>, 193 _kind: UnwindInfoKind, 194 ) -> Result<Option<UnwindInfo>>; 195 196 /// See `cranelift_codegen::isa::TargetIsa::create_systemv_cie`. 197 fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { 198 // By default, an ISA cannot create a System V CIE. 199 None 200 } 201 202 /// See `cranelift_codegen::isa::TargetIsa::text_section_builder`. 203 fn text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>; 204 205 /// See `cranelift_codegen::isa::TargetIsa::function_alignment`. 206 fn function_alignment(&self) -> u32; 207 208 /// Returns the pointer width of the ISA in bytes. 209 fn pointer_bytes(&self) -> u8 { 210 let width = self.triple().pointer_width().unwrap(); 211 width.bytes() 212 } 213 214 /// The log2 of the target's page size and alignment. 215 /// 216 /// Note that this may be an upper-bound that is larger than necessary for 217 /// some platforms since it may depend on runtime configuration. 218 fn page_size_align_log2(&self) -> u8; 219 } 220 221 impl Debug for &dyn TargetIsa { 222 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 223 write!( 224 f, 225 "Target ISA {{ triple: {:?}, calling convention: {:?} }}", 226 self.triple(), 227 self.default_call_conv() 228 ) 229 } 230 } 231 232 /// Per-class register environment. 233 pub(crate) struct RegClassEnv { 234 /// Float register class limit. 235 limit: u8, 236 /// Float register class index. 237 index: u8, 238 } 239 240 /// Helper environment to track register assignment for Winch's default calling 241 /// convention. 242 pub(crate) struct RegIndexEnv { 243 /// Int register environment. 244 int: RegClassEnv, 245 /// Float register environment. 246 float: Option<RegClassEnv>, 247 } 248 249 impl RegIndexEnv { 250 fn with_limits_per_class(int: u8, float: u8) -> Self { 251 let int = RegClassEnv { 252 limit: int, 253 index: 0, 254 }; 255 256 let float = RegClassEnv { 257 limit: float, 258 index: 0, 259 }; 260 261 Self { 262 int, 263 float: Some(float), 264 } 265 } 266 267 fn with_absolute_limit(limit: u8) -> Self { 268 let int = RegClassEnv { limit, index: 0 }; 269 270 Self { int, float: None } 271 } 272 } 273 274 impl RegIndexEnv { 275 fn next_gpr(&mut self) -> Option<u8> { 276 (self.int.index < self.int.limit) 277 .then(|| Self::increment(&mut self.int.index)) 278 .flatten() 279 } 280 281 fn next_fpr(&mut self) -> Option<u8> { 282 if let Some(f) = self.float.as_mut() { 283 (f.index < f.limit) 284 .then(|| Self::increment(&mut f.index)) 285 .flatten() 286 } else { 287 // If a single `RegClassEnv` is used, it means that the count is 288 // absolute, so we default to calling `next_gpr`. 289 self.next_gpr() 290 } 291 } 292 293 fn increment(index: &mut u8) -> Option<u8> { 294 let current = *index; 295 match index.checked_add(1) { 296 Some(next) => { 297 *index = next; 298 Some(current) 299 } 300 None => None, 301 } 302 } 303 } 304 305 #[cfg(test)] 306 mod tests { 307 use super::RegIndexEnv; 308 #[test] 309 fn test_get_next_reg_index() { 310 let mut index_env = RegIndexEnv::with_limits_per_class(3, 3); 311 assert_eq!(index_env.next_fpr(), Some(0)); 312 assert_eq!(index_env.next_gpr(), Some(0)); 313 assert_eq!(index_env.next_fpr(), Some(1)); 314 assert_eq!(index_env.next_gpr(), Some(1)); 315 assert_eq!(index_env.next_fpr(), Some(2)); 316 assert_eq!(index_env.next_gpr(), Some(2)); 317 } 318 319 #[test] 320 fn test_reg_index_env_absolute_count() { 321 let mut e = RegIndexEnv::with_absolute_limit(4); 322 assert!(e.next_gpr() == Some(0)); 323 assert!(e.next_fpr() == Some(1)); 324 assert!(e.next_gpr() == Some(2)); 325 assert!(e.next_fpr() == Some(3)); 326 } 327 } 328