xref: /wasmtime-44.0.1/winch/codegen/src/isa/mod.rs (revision f9f8a4df)
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