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