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