1 use crate::{BuiltinFunctions, 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, ModuleTypesBuilder, 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 a variation of 74 /// the calling conventions defined in this enum plus 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 /// 81 /// The main difference between the system calling conventions defined in 82 /// this enum and their native counterparts is how multiple returns are handled. 83 /// Given that Winch is not meant to be a standalone code generator, the code 84 /// it generates is tightly coupled to how Wasmtime expects multiple returns 85 /// to be handled: the first return in a register, dictated by the calling 86 /// convention and the rest, if any, via a return pointer. 87 #[derive(Copy, Clone, Debug)] 88 pub enum CallingConvention { 89 /// See [cranelift_codegen::isa::CallConv::WasmtimeSystemV] 90 SystemV, 91 /// See [cranelift_codegen::isa::CallConv::WindowsFastcall] 92 WindowsFastcall, 93 /// See [cranelift_codegen::isa::CallConv::AppleAarch64] 94 AppleAarch64, 95 /// The default calling convention for Winch. It largely follows SystemV 96 /// for parameter and result handling. This calling convention is part of 97 /// Winch's default ABI [crate::abi::ABI]. 98 Default, 99 } 100 101 impl CallingConvention { 102 /// Returns true if the current calling convention is `WasmtimeFastcall`. 103 fn is_fastcall(&self) -> bool { 104 match &self { 105 CallingConvention::WindowsFastcall => true, 106 _ => false, 107 } 108 } 109 110 /// Returns true if the current calling convention is `WasmtimeSystemV`. 111 fn is_systemv(&self) -> bool { 112 match &self { 113 CallingConvention::SystemV => true, 114 _ => false, 115 } 116 } 117 118 /// Returns true if the current calling convention is `WasmtimeAppleAarch64`. 119 fn is_apple_aarch64(&self) -> bool { 120 match &self { 121 CallingConvention::AppleAarch64 => true, 122 _ => false, 123 } 124 } 125 126 /// Returns true if the current calling convention is `Default`. 127 pub fn is_default(&self) -> bool { 128 match &self { 129 CallingConvention::Default => true, 130 _ => false, 131 } 132 } 133 } 134 135 /// A trait representing commonalities between the supported 136 /// instruction set architectures. 137 pub trait TargetIsa: Send + Sync { 138 /// Get the name of the ISA. 139 fn name(&self) -> &'static str; 140 141 /// Get the target triple of the ISA. 142 fn triple(&self) -> &Triple; 143 144 /// Get the ISA-independent flags that were used to make this trait object. 145 fn flags(&self) -> &settings::Flags; 146 147 /// Get the ISA-dependent flag values that were used to make this trait object. 148 fn isa_flags(&self) -> Vec<settings::Value>; 149 150 /// Get a flag indicating whether branch protection is enabled. 151 fn is_branch_protection_enabled(&self) -> bool { 152 false 153 } 154 155 /// Compile a function. 156 fn compile_function( 157 &self, 158 sig: &WasmFuncType, 159 body: &FunctionBody, 160 translation: &ModuleTranslation, 161 types: &ModuleTypesBuilder, 162 builtins: &mut BuiltinFunctions, 163 validator: &mut FuncValidator<ValidatorResources>, 164 ) -> Result<MachBufferFinalized<Final>>; 165 166 /// Get the default calling convention of the underlying target triple. 167 fn default_call_conv(&self) -> CallConv { 168 CallConv::triple_default(&self.triple()) 169 } 170 171 /// Derive Wasmtime's calling convention from the triple's default 172 /// calling convention. 173 fn wasmtime_call_conv(&self) -> CallingConvention { 174 match self.default_call_conv() { 175 CallConv::AppleAarch64 => CallingConvention::AppleAarch64, 176 CallConv::SystemV => CallingConvention::SystemV, 177 CallConv::WindowsFastcall => CallingConvention::WindowsFastcall, 178 cc => unimplemented!("calling convention: {:?}", cc), 179 } 180 } 181 182 /// Get the endianess of the underlying target triple. 183 fn endianness(&self) -> target_lexicon::Endianness { 184 self.triple().endianness().unwrap() 185 } 186 187 /// See `cranelift_codegen::isa::TargetIsa::create_systemv_cie`. 188 fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { 189 // By default, an ISA cannot create a System V CIE. 190 None 191 } 192 193 /// See `cranelift_codegen::isa::TargetIsa::text_section_builder`. 194 fn text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>; 195 196 /// See `cranelift_codegen::isa::TargetIsa::function_alignment`. 197 fn function_alignment(&self) -> u32; 198 199 /// Compile a trampoline kind. 200 /// 201 /// This function, internally dispatches to the right trampoline to emit 202 /// depending on the `kind` paramter. 203 fn compile_trampoline( 204 &self, 205 ty: &WasmFuncType, 206 kind: TrampolineKind, 207 ) -> Result<MachBufferFinalized<Final>>; 208 209 /// Returns the pointer width of the ISA in bytes. 210 fn pointer_bytes(&self) -> u8 { 211 let width = self.triple().pointer_width().unwrap(); 212 width.bytes() 213 } 214 } 215 216 impl Debug for &dyn TargetIsa { 217 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 218 write!( 219 f, 220 "Target ISA {{ triple: {:?}, calling convention: {:?} }}", 221 self.triple(), 222 self.default_call_conv() 223 ) 224 } 225 } 226