1 use crate::{BuiltinFunctions, TrampolineKind}; 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_environ::{ModuleTranslation, ModuleTypesBuilder, 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(anyhow!(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(anyhow!(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)] 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::WasmtimeSystemV] 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 `WasmtimeFastcall`. 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 `WasmtimeSystemV`. 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 `WasmtimeAppleAarch64`. 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 /// A trait representing commonalities between the supported 137 /// instruction set architectures. 138 pub trait TargetIsa: Send + Sync { 139 /// Get the name of the ISA. 140 fn name(&self) -> &'static str; 141 142 /// Get the target triple of the ISA. 143 fn triple(&self) -> &Triple; 144 145 /// Get the ISA-independent flags that were used to make this trait object. 146 fn flags(&self) -> &settings::Flags; 147 148 /// Get the ISA-dependent flag values that were used to make this trait object. 149 fn isa_flags(&self) -> Vec<settings::Value>; 150 151 /// Get a flag indicating whether branch protection is enabled. 152 fn is_branch_protection_enabled(&self) -> bool { 153 false 154 } 155 156 /// Compile a function. 157 fn compile_function( 158 &self, 159 sig: &WasmFuncType, 160 body: &FunctionBody, 161 translation: &ModuleTranslation, 162 types: &ModuleTypesBuilder, 163 builtins: &mut BuiltinFunctions, 164 validator: &mut FuncValidator<ValidatorResources>, 165 ) -> Result<MachBufferFinalized<Final>>; 166 167 /// Get the default calling convention of the underlying target triple. 168 fn default_call_conv(&self) -> CallConv { 169 CallConv::triple_default(&self.triple()) 170 } 171 172 /// Derive Wasmtime's calling convention from the triple's default 173 /// calling convention. 174 fn wasmtime_call_conv(&self) -> CallingConvention { 175 match self.default_call_conv() { 176 CallConv::AppleAarch64 => CallingConvention::AppleAarch64, 177 CallConv::SystemV => CallingConvention::SystemV, 178 CallConv::WindowsFastcall => CallingConvention::WindowsFastcall, 179 cc => unimplemented!("calling convention: {:?}", cc), 180 } 181 } 182 183 /// Get the endianess of the underlying target triple. 184 fn endianness(&self) -> target_lexicon::Endianness { 185 self.triple().endianness().unwrap() 186 } 187 188 fn emit_unwind_info( 189 &self, 190 _result: &MachBufferFinalized<Final>, 191 _kind: UnwindInfoKind, 192 ) -> Result<Option<UnwindInfo>>; 193 194 /// See `cranelift_codegen::isa::TargetIsa::create_systemv_cie`. 195 fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { 196 // By default, an ISA cannot create a System V CIE. 197 None 198 } 199 200 /// See `cranelift_codegen::isa::TargetIsa::text_section_builder`. 201 fn text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>; 202 203 /// See `cranelift_codegen::isa::TargetIsa::function_alignment`. 204 fn function_alignment(&self) -> u32; 205 206 /// Compile a trampoline kind. 207 /// 208 /// This function, internally dispatches to the right trampoline to emit 209 /// depending on the `kind` paramter. 210 fn compile_trampoline( 211 &self, 212 ty: &WasmFuncType, 213 kind: TrampolineKind, 214 ) -> Result<MachBufferFinalized<Final>>; 215 216 /// Returns the pointer width of the ISA in bytes. 217 fn pointer_bytes(&self) -> u8 { 218 let width = self.triple().pointer_width().unwrap(); 219 width.bytes() 220 } 221 } 222 223 impl Debug for &dyn TargetIsa { 224 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 225 write!( 226 f, 227 "Target ISA {{ triple: {:?}, calling convention: {:?} }}", 228 self.triple(), 229 self.default_call_conv() 230 ) 231 } 232 } 233