1 use crate::TrampolineKind; 2 use anyhow::{anyhow, Result}; 3 use core::fmt::Formatter; 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_environ::{ModuleTranslation, WasmFuncType}; 14 15 #[cfg(feature = "x64")] 16 pub(crate) mod x64; 17 18 #[cfg(feature = "arm64")] 19 pub(crate) mod aarch64; 20 21 pub(crate) mod reg; 22 23 macro_rules! isa_builder { 24 ($name: ident, $cfg_terms: tt, $triple: ident) => {{ 25 #[cfg $cfg_terms] 26 { 27 Ok($name::isa_builder($triple)) 28 } 29 #[cfg(not $cfg_terms)] 30 { 31 Err(anyhow!(LookupError::SupportDisabled)) 32 } 33 }}; 34 } 35 36 pub type Builder = IsaBuilder<Result<Box<dyn TargetIsa>>>; 37 38 /// Look for an ISA builder for the given target triple. 39 pub fn lookup(triple: Triple) -> Result<Builder> { 40 match triple.architecture { 41 Architecture::X86_64 => { 42 isa_builder!(x64, (feature = "x64"), triple) 43 } 44 Architecture::Aarch64 { .. } => { 45 isa_builder!(aarch64, (feature = "arm64"), triple) 46 } 47 48 _ => Err(anyhow!(LookupError::Unsupported)), 49 } 50 } 51 52 impl error::Error for LookupError {} 53 impl Display for LookupError { 54 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 55 match self { 56 LookupError::Unsupported => write!(f, "This target is not supported yet"), 57 LookupError::SupportDisabled => write!(f, "Support for this target was disabled"), 58 } 59 } 60 } 61 62 #[derive(Debug)] 63 pub(crate) enum LookupError { 64 Unsupported, 65 // This directive covers the case in which the consumer 66 // enables the `all-arch` feature; in such case, this variant 67 // will never be used. This is most likely going to change 68 // in the future; this is one of the simplest options for now. 69 #[allow(dead_code)] 70 SupportDisabled, 71 } 72 73 /// Calling conventions supported by Winch. Winch supports the `Wasmtime*` 74 /// variations of the system's ABI calling conventions and an internal default 75 /// calling convention. 76 /// 77 /// This enum is a reduced subset of the calling conventions defined in 78 /// [cranelift_codegen::isa::CallConv]. Introducing this enum makes it easier 79 /// to enforce the invariant of all the calling conventions supported by Winch. 80 pub enum CallingConvention { 81 /// See [cranelift_codegen::isa::CallConv::WasmtimeSystemV] 82 WasmtimeSystemV, 83 /// See [cranelift_codegen::isa::CallConv::WindowsFastcall] 84 WindowsFastcall, 85 /// See [cranelift_codegen::isa::CallConv::AppleAarch64] 86 AppleAarch64, 87 /// The default calling convention for Winch. It largely follows SystemV 88 /// for parameter and result handling. This calling convention is part of 89 /// Winch's default ABI [crate::abi::ABI]. 90 Default, 91 } 92 93 impl CallingConvention { 94 /// Returns true if the current calling convention is `WasmtimeFastcall`. 95 fn is_fastcall(&self) -> bool { 96 match &self { 97 CallingConvention::WindowsFastcall => true, 98 _ => false, 99 } 100 } 101 102 /// Returns true if the current calling convention is `WasmtimeSystemV`. 103 fn is_systemv(&self) -> bool { 104 match &self { 105 CallingConvention::WasmtimeSystemV => true, 106 _ => false, 107 } 108 } 109 110 /// Returns true if the current calling convention is `WasmtimeAppleAarch64`. 111 fn is_apple_aarch64(&self) -> bool { 112 match &self { 113 CallingConvention::AppleAarch64 => true, 114 _ => false, 115 } 116 } 117 118 /// Returns true if the current calling convention is `Default`. 119 fn is_default(&self) -> bool { 120 match &self { 121 CallingConvention::Default => true, 122 _ => false, 123 } 124 } 125 } 126 127 /// A trait representing commonalities between the supported 128 /// instruction set architectures. 129 pub trait TargetIsa: Send + Sync { 130 /// Get the name of the ISA. 131 fn name(&self) -> &'static str; 132 133 /// Get the target triple of the ISA. 134 fn triple(&self) -> &Triple; 135 136 /// Get the ISA-independent flags that were used to make this trait object. 137 fn flags(&self) -> &settings::Flags; 138 139 /// Get the ISA-dependent flag values that were used to make this trait object. 140 fn isa_flags(&self) -> Vec<settings::Value>; 141 142 /// Get a flag indicating whether branch protection is enabled. 143 fn is_branch_protection_enabled(&self) -> bool { 144 false 145 } 146 147 /// Compile a function. 148 fn compile_function( 149 &self, 150 sig: &WasmFuncType, 151 body: &FunctionBody, 152 translation: &ModuleTranslation, 153 validator: &mut FuncValidator<ValidatorResources>, 154 ) -> Result<MachBufferFinalized<Final>>; 155 156 /// Get the default calling convention of the underlying target triple. 157 fn default_call_conv(&self) -> CallConv { 158 CallConv::triple_default(&self.triple()) 159 } 160 161 /// Derive Wasmtime's calling convention from the triple's default 162 /// calling convention. 163 fn wasmtime_call_conv(&self) -> CallingConvention { 164 match self.default_call_conv() { 165 CallConv::AppleAarch64 => CallingConvention::AppleAarch64, 166 CallConv::SystemV => CallingConvention::WasmtimeSystemV, 167 CallConv::WindowsFastcall => CallingConvention::WindowsFastcall, 168 cc => unimplemented!("calling convention: {:?}", cc), 169 } 170 } 171 172 /// Get the endianess of the underlying target triple. 173 fn endianness(&self) -> target_lexicon::Endianness { 174 self.triple().endianness().unwrap() 175 } 176 177 /// See `cranelift_codegen::isa::TargetIsa::create_systemv_cie`. 178 fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { 179 // By default, an ISA cannot create a System V CIE. 180 None 181 } 182 183 /// See `cranelift_codegen::isa::TargetIsa::text_section_builder`. 184 fn text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>; 185 186 /// See `cranelift_codegen::isa::TargetIsa::function_alignment`. 187 fn function_alignment(&self) -> u32; 188 189 /// Compile a trampoline kind. 190 /// 191 /// This function, internally dispatches to the right trampoline to emit 192 /// depending on the `kind` paramter. 193 fn compile_trampoline( 194 &self, 195 ty: &WasmFuncType, 196 kind: TrampolineKind, 197 ) -> Result<MachBufferFinalized<Final>>; 198 199 /// Returns the pointer width of the ISA in bytes. 200 fn pointer_bytes(&self) -> u8 { 201 let width = self.triple().pointer_width().unwrap(); 202 width.bytes() 203 } 204 } 205 206 impl Debug for &dyn TargetIsa { 207 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 208 write!( 209 f, 210 "Target ISA {{ triple: {:?}, calling convention: {:?} }}", 211 self.triple(), 212 self.default_call_conv() 213 ) 214 } 215 } 216