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 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 { 55 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`. 104 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`. 112 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`. 120 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`. 128 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 { 137 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. 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 fn is_branch_protection_enabled(&self) -> bool { 164 false 165 } 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 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. 186 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. 196 fn endianness(&self) -> target_lexicon::Endianness { 197 self.triple().endianness().unwrap() 198 } 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 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`. 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 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. 228 fn page_size_align_log2(&self) -> u8; 229 } 230 231 impl Debug for &dyn TargetIsa { 232 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 { 260 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 277 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 { 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 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 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] 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 } 338