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 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