1*b112bb85SNick Fitzgerald use crate::{BuiltinFunctions, Result, format_err};
2835abbcdSSaúl Cabrera use core::fmt::Formatter;
30bcceda3STrevor Elliott use cranelift_codegen::isa::unwind::{UnwindInfo, UnwindInfoKind};
4013b35ffSKevin Rizzo use cranelift_codegen::isa::{CallConv, IsaBuilder};
5013b35ffSKevin Rizzo use cranelift_codegen::settings;
6013b35ffSKevin Rizzo use cranelift_codegen::{Final, MachBufferFinalized, TextSectionBuilder};
7835abbcdSSaúl Cabrera use std::{
8835abbcdSSaúl Cabrera error,
9835abbcdSSaúl Cabrera fmt::{self, Debug, Display},
10835abbcdSSaúl Cabrera };
11835abbcdSSaúl Cabrera use target_lexicon::{Architecture, Triple};
1292024ad1SLuna P-C use wasmparser::{FuncValidator, FunctionBody, ValidatorResources};
13355990b4SAlex Crichton use wasmtime_cranelift::CompiledFunction;
142f684ba1SSaúl Cabrera use wasmtime_environ::{ModuleTranslation, ModuleTypesBuilder, Tunables, WasmFuncType};
15af4d94c8SSaúl Cabrera
16835abbcdSSaúl Cabrera #[cfg(feature = "x64")]
17835abbcdSSaúl Cabrera pub(crate) mod x64;
18835abbcdSSaúl Cabrera
19835abbcdSSaúl Cabrera #[cfg(feature = "arm64")]
20835abbcdSSaúl Cabrera pub(crate) mod aarch64;
21835abbcdSSaúl Cabrera
22835abbcdSSaúl Cabrera pub(crate) mod reg;
23835abbcdSSaúl Cabrera
2494b51cdbSSaúl Cabrera macro_rules! isa_builder {
25835abbcdSSaúl Cabrera ($name: ident, $cfg_terms: tt, $triple: ident) => {{
26835abbcdSSaúl Cabrera #[cfg $cfg_terms]
27835abbcdSSaúl Cabrera {
2894b51cdbSSaúl Cabrera Ok($name::isa_builder($triple))
29835abbcdSSaúl Cabrera }
30835abbcdSSaúl Cabrera #[cfg(not $cfg_terms)]
31835abbcdSSaúl Cabrera {
32*b112bb85SNick Fitzgerald Err(format_err!(LookupError::SupportDisabled))
33835abbcdSSaúl Cabrera }
34835abbcdSSaúl Cabrera }};
35835abbcdSSaúl Cabrera }
36835abbcdSSaúl Cabrera
37013b35ffSKevin Rizzo pub type Builder = IsaBuilder<Result<Box<dyn TargetIsa>>>;
3894b51cdbSSaúl Cabrera
3994b51cdbSSaúl Cabrera /// Look for an ISA builder for the given target triple.
lookup(triple: Triple) -> Result<Builder>4094b51cdbSSaúl Cabrera pub fn lookup(triple: Triple) -> Result<Builder> {
41835abbcdSSaúl Cabrera match triple.architecture {
42835abbcdSSaúl Cabrera Architecture::X86_64 => {
4394b51cdbSSaúl Cabrera isa_builder!(x64, (feature = "x64"), triple)
44835abbcdSSaúl Cabrera }
45835abbcdSSaúl Cabrera Architecture::Aarch64 { .. } => {
4694b51cdbSSaúl Cabrera isa_builder!(aarch64, (feature = "arm64"), triple)
47835abbcdSSaúl Cabrera }
48835abbcdSSaúl Cabrera
49*b112bb85SNick Fitzgerald _ => Err(format_err!(LookupError::Unsupported)),
50835abbcdSSaúl Cabrera }
51835abbcdSSaúl Cabrera }
52835abbcdSSaúl Cabrera
53835abbcdSSaúl Cabrera impl error::Error for LookupError {}
54835abbcdSSaúl Cabrera impl Display for LookupError {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result55835abbcdSSaúl Cabrera fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
56835abbcdSSaúl Cabrera match self {
57835abbcdSSaúl Cabrera LookupError::Unsupported => write!(f, "This target is not supported yet"),
58835abbcdSSaúl Cabrera LookupError::SupportDisabled => write!(f, "Support for this target was disabled"),
59835abbcdSSaúl Cabrera }
60835abbcdSSaúl Cabrera }
61835abbcdSSaúl Cabrera }
62835abbcdSSaúl Cabrera
63835abbcdSSaúl Cabrera #[derive(Debug)]
64835abbcdSSaúl Cabrera pub(crate) enum LookupError {
65835abbcdSSaúl Cabrera Unsupported,
66835abbcdSSaúl Cabrera // This directive covers the case in which the consumer
67835abbcdSSaúl Cabrera // enables the `all-arch` feature; in such case, this variant
68835abbcdSSaúl Cabrera // will never be used. This is most likely going to change
69835abbcdSSaúl Cabrera // in the future; this is one of the simplest options for now.
707a66c39aSAlex Crichton #[allow(dead_code, reason = "see comment")]
71835abbcdSSaúl Cabrera SupportDisabled,
72835abbcdSSaúl Cabrera }
73835abbcdSSaúl Cabrera
74f0162a40SSaúl Cabrera /// Calling conventions supported by Winch. Winch supports a variation of
75f0162a40SSaúl Cabrera /// the calling conventions defined in this enum plus an internal default
769dd0b59cSSaúl Cabrera /// calling convention.
779dd0b59cSSaúl Cabrera ///
789dd0b59cSSaúl Cabrera /// This enum is a reduced subset of the calling conventions defined in
799dd0b59cSSaúl Cabrera /// [cranelift_codegen::isa::CallConv]. Introducing this enum makes it easier
809dd0b59cSSaúl Cabrera /// to enforce the invariant of all the calling conventions supported by Winch.
81f0162a40SSaúl Cabrera ///
82f0162a40SSaúl Cabrera /// The main difference between the system calling conventions defined in
83f0162a40SSaúl Cabrera /// this enum and their native counterparts is how multiple returns are handled.
84f0162a40SSaúl Cabrera /// Given that Winch is not meant to be a standalone code generator, the code
85f0162a40SSaúl Cabrera /// it generates is tightly coupled to how Wasmtime expects multiple returns
86f0162a40SSaúl Cabrera /// to be handled: the first return in a register, dictated by the calling
87f0162a40SSaúl Cabrera /// convention and the rest, if any, via a return pointer.
88f0162a40SSaúl Cabrera #[derive(Copy, Clone, Debug)]
899dd0b59cSSaúl Cabrera pub enum CallingConvention {
90a659b5a9Sbjorn3 /// See [cranelift_codegen::isa::CallConv::SystemV]
91f0162a40SSaúl Cabrera SystemV,
9239c96c7aSAlex Crichton /// See [cranelift_codegen::isa::CallConv::WindowsFastcall]
9339c96c7aSAlex Crichton WindowsFastcall,
9439c96c7aSAlex Crichton /// See [cranelift_codegen::isa::CallConv::AppleAarch64]
9539c96c7aSAlex Crichton AppleAarch64,
969dd0b59cSSaúl Cabrera /// The default calling convention for Winch. It largely follows SystemV
979dd0b59cSSaúl Cabrera /// for parameter and result handling. This calling convention is part of
98d38d387aSAlex Crichton /// Winch's default ABI `crate::abi::ABI`.
999dd0b59cSSaúl Cabrera Default,
1009dd0b59cSSaúl Cabrera }
1019dd0b59cSSaúl Cabrera
1029dd0b59cSSaúl Cabrera impl CallingConvention {
103a659b5a9Sbjorn3 /// Returns true if the current calling convention is `WindowsFastcall`.
is_fastcall(&self) -> bool1049dd0b59cSSaúl Cabrera fn is_fastcall(&self) -> bool {
1059dd0b59cSSaúl Cabrera match &self {
10639c96c7aSAlex Crichton CallingConvention::WindowsFastcall => true,
1079dd0b59cSSaúl Cabrera _ => false,
1089dd0b59cSSaúl Cabrera }
1099dd0b59cSSaúl Cabrera }
1109dd0b59cSSaúl Cabrera
111a659b5a9Sbjorn3 /// Returns true if the current calling convention is `SystemV`.
is_systemv(&self) -> bool1129dd0b59cSSaúl Cabrera fn is_systemv(&self) -> bool {
1139dd0b59cSSaúl Cabrera match &self {
114f0162a40SSaúl Cabrera CallingConvention::SystemV => true,
1159dd0b59cSSaúl Cabrera _ => false,
1169dd0b59cSSaúl Cabrera }
1179dd0b59cSSaúl Cabrera }
1189dd0b59cSSaúl Cabrera
119a659b5a9Sbjorn3 /// Returns true if the current calling convention is `AppleAarch64`.
is_apple_aarch64(&self) -> bool1209dd0b59cSSaúl Cabrera fn is_apple_aarch64(&self) -> bool {
1219dd0b59cSSaúl Cabrera match &self {
12239c96c7aSAlex Crichton CallingConvention::AppleAarch64 => true,
1239dd0b59cSSaúl Cabrera _ => false,
1249dd0b59cSSaúl Cabrera }
1259dd0b59cSSaúl Cabrera }
1269dd0b59cSSaúl Cabrera
1279dd0b59cSSaúl Cabrera /// Returns true if the current calling convention is `Default`.
is_default(&self) -> bool128f0162a40SSaúl Cabrera pub fn is_default(&self) -> bool {
1299dd0b59cSSaúl Cabrera match &self {
1309dd0b59cSSaúl Cabrera CallingConvention::Default => true,
1319dd0b59cSSaúl Cabrera _ => false,
1329dd0b59cSSaúl Cabrera }
1339dd0b59cSSaúl Cabrera }
1349dd0b59cSSaúl Cabrera }
1359dd0b59cSSaúl Cabrera
136792cccceSSaúl Cabrera impl From<CallingConvention> for CallConv {
from(value: CallingConvention) -> Self137792cccceSSaúl Cabrera fn from(value: CallingConvention) -> Self {
138792cccceSSaúl Cabrera match value {
139792cccceSSaúl Cabrera CallingConvention::SystemV => Self::SystemV,
140792cccceSSaúl Cabrera CallingConvention::AppleAarch64 => Self::AppleAarch64,
141792cccceSSaúl Cabrera CallingConvention::Default => Self::Winch,
142792cccceSSaúl Cabrera CallingConvention::WindowsFastcall => Self::WindowsFastcall,
143792cccceSSaúl Cabrera }
144792cccceSSaúl Cabrera }
145792cccceSSaúl Cabrera }
146792cccceSSaúl Cabrera
147835abbcdSSaúl Cabrera /// A trait representing commonalities between the supported
148835abbcdSSaúl Cabrera /// instruction set architectures.
149835abbcdSSaúl Cabrera pub trait TargetIsa: Send + Sync {
150835abbcdSSaúl Cabrera /// Get the name of the ISA.
name(&self) -> &'static str151835abbcdSSaúl Cabrera fn name(&self) -> &'static str;
152835abbcdSSaúl Cabrera
153835abbcdSSaúl Cabrera /// Get the target triple of the ISA.
triple(&self) -> &Triple154835abbcdSSaúl Cabrera fn triple(&self) -> &Triple;
155835abbcdSSaúl Cabrera
156013b35ffSKevin Rizzo /// Get the ISA-independent flags that were used to make this trait object.
flags(&self) -> &settings::Flags157013b35ffSKevin Rizzo fn flags(&self) -> &settings::Flags;
158013b35ffSKevin Rizzo
159013b35ffSKevin Rizzo /// Get the ISA-dependent flag values that were used to make this trait object.
isa_flags(&self) -> Vec<settings::Value>160013b35ffSKevin Rizzo fn isa_flags(&self) -> Vec<settings::Value>;
161013b35ffSKevin Rizzo
162013b35ffSKevin Rizzo /// Get a flag indicating whether branch protection is enabled.
is_branch_protection_enabled(&self) -> bool163013b35ffSKevin Rizzo fn is_branch_protection_enabled(&self) -> bool {
164013b35ffSKevin Rizzo false
165013b35ffSKevin Rizzo }
166013b35ffSKevin Rizzo
1673a92aa3dSKevin Rizzo /// Compile a function.
compile_function( &self, sig: &WasmFuncType, body: &FunctionBody, translation: &ModuleTranslation, types: &ModuleTypesBuilder, builtins: &mut BuiltinFunctions, validator: &mut FuncValidator<ValidatorResources>, tunables: &Tunables, ) -> Result<CompiledFunction>168835abbcdSSaúl Cabrera fn compile_function(
169835abbcdSSaúl Cabrera &self,
17092024ad1SLuna P-C sig: &WasmFuncType,
1713b966855SAlex Crichton body: &FunctionBody,
172afde47c2SSaúl Cabrera translation: &ModuleTranslation,
173ef07f40fSAlex Crichton types: &ModuleTypesBuilder,
1744f47f3ecSSaúl Cabrera builtins: &mut BuiltinFunctions,
1753a92aa3dSKevin Rizzo validator: &mut FuncValidator<ValidatorResources>,
1762f684ba1SSaúl Cabrera tunables: &Tunables,
177355990b4SAlex Crichton ) -> Result<CompiledFunction>;
178835abbcdSSaúl Cabrera
179835abbcdSSaúl Cabrera /// Get the default calling convention of the underlying target triple.
default_call_conv(&self) -> CallConv1809dd0b59cSSaúl Cabrera fn default_call_conv(&self) -> CallConv {
181835abbcdSSaúl Cabrera CallConv::triple_default(&self.triple())
182835abbcdSSaúl Cabrera }
183835abbcdSSaúl Cabrera
1849dd0b59cSSaúl Cabrera /// Derive Wasmtime's calling convention from the triple's default
1859dd0b59cSSaúl Cabrera /// calling convention.
wasmtime_call_conv(&self) -> CallingConvention1869dd0b59cSSaúl Cabrera fn wasmtime_call_conv(&self) -> CallingConvention {
1879dd0b59cSSaúl Cabrera match self.default_call_conv() {
18839c96c7aSAlex Crichton CallConv::AppleAarch64 => CallingConvention::AppleAarch64,
189f0162a40SSaúl Cabrera CallConv::SystemV => CallingConvention::SystemV,
19039c96c7aSAlex Crichton CallConv::WindowsFastcall => CallingConvention::WindowsFastcall,
1919dd0b59cSSaúl Cabrera cc => unimplemented!("calling convention: {:?}", cc),
1929dd0b59cSSaúl Cabrera }
1939dd0b59cSSaúl Cabrera }
1949dd0b59cSSaúl Cabrera
1955036bb0bSRoboSchmied /// Get the endianness of the underlying target triple.
endianness(&self) -> target_lexicon::Endianness196835abbcdSSaúl Cabrera fn endianness(&self) -> target_lexicon::Endianness {
197835abbcdSSaúl Cabrera self.triple().endianness().unwrap()
198835abbcdSSaúl Cabrera }
199013b35ffSKevin Rizzo
emit_unwind_info( &self, _result: &MachBufferFinalized<Final>, _kind: UnwindInfoKind, ) -> Result<Option<UnwindInfo>>2000bcceda3STrevor Elliott fn emit_unwind_info(
2010bcceda3STrevor Elliott &self,
2020bcceda3STrevor Elliott _result: &MachBufferFinalized<Final>,
2030bcceda3STrevor Elliott _kind: UnwindInfoKind,
2040bcceda3STrevor Elliott ) -> Result<Option<UnwindInfo>>;
2050bcceda3STrevor Elliott
206013b35ffSKevin Rizzo /// See `cranelift_codegen::isa::TargetIsa::create_systemv_cie`.
create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry>207013b35ffSKevin Rizzo fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
208013b35ffSKevin Rizzo // By default, an ISA cannot create a System V CIE.
209013b35ffSKevin Rizzo None
210013b35ffSKevin Rizzo }
211013b35ffSKevin Rizzo
212013b35ffSKevin Rizzo /// See `cranelift_codegen::isa::TargetIsa::text_section_builder`.
text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>213013b35ffSKevin Rizzo fn text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>;
214013b35ffSKevin Rizzo
215013b35ffSKevin Rizzo /// See `cranelift_codegen::isa::TargetIsa::function_alignment`.
function_alignment(&self) -> u32216013b35ffSKevin Rizzo fn function_alignment(&self) -> u32;
2173a92aa3dSKevin Rizzo
21820c58362SSaúl Cabrera /// Returns the pointer width of the ISA in bytes.
pointer_bytes(&self) -> u821920c58362SSaúl Cabrera fn pointer_bytes(&self) -> u8 {
22020c58362SSaúl Cabrera let width = self.triple().pointer_width().unwrap();
22120c58362SSaúl Cabrera width.bytes()
22220c58362SSaúl Cabrera }
223194b4803SAlex Crichton
224194b4803SAlex Crichton /// The log2 of the target's page size and alignment.
225194b4803SAlex Crichton ///
226194b4803SAlex Crichton /// Note that this may be an upper-bound that is larger than necessary for
227194b4803SAlex Crichton /// some platforms since it may depend on runtime configuration.
page_size_align_log2(&self) -> u8228194b4803SAlex Crichton fn page_size_align_log2(&self) -> u8;
229835abbcdSSaúl Cabrera }
230835abbcdSSaúl Cabrera
231835abbcdSSaúl Cabrera impl Debug for &dyn TargetIsa {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result232835abbcdSSaúl Cabrera fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
233835abbcdSSaúl Cabrera write!(
234835abbcdSSaúl Cabrera f,
235835abbcdSSaúl Cabrera "Target ISA {{ triple: {:?}, calling convention: {:?} }}",
236835abbcdSSaúl Cabrera self.triple(),
2379dd0b59cSSaúl Cabrera self.default_call_conv()
238835abbcdSSaúl Cabrera )
239835abbcdSSaúl Cabrera }
240835abbcdSSaúl Cabrera }
241721600caSSaúl Cabrera
242721600caSSaúl Cabrera /// Per-class register environment.
243721600caSSaúl Cabrera pub(crate) struct RegClassEnv {
244721600caSSaúl Cabrera /// Float register class limit.
245721600caSSaúl Cabrera limit: u8,
246721600caSSaúl Cabrera /// Float register class index.
247721600caSSaúl Cabrera index: u8,
248721600caSSaúl Cabrera }
249721600caSSaúl Cabrera
250721600caSSaúl Cabrera /// Helper environment to track register assignment for Winch's default calling
251721600caSSaúl Cabrera /// convention.
252721600caSSaúl Cabrera pub(crate) struct RegIndexEnv {
253721600caSSaúl Cabrera /// Int register environment.
254721600caSSaúl Cabrera int: RegClassEnv,
255721600caSSaúl Cabrera /// Float register environment.
256721600caSSaúl Cabrera float: Option<RegClassEnv>,
257721600caSSaúl Cabrera }
258721600caSSaúl Cabrera
259721600caSSaúl Cabrera impl RegIndexEnv {
with_limits_per_class(int: u8, float: u8) -> Self260721600caSSaúl Cabrera fn with_limits_per_class(int: u8, float: u8) -> Self {
261721600caSSaúl Cabrera let int = RegClassEnv {
262721600caSSaúl Cabrera limit: int,
263721600caSSaúl Cabrera index: 0,
264721600caSSaúl Cabrera };
265721600caSSaúl Cabrera
266721600caSSaúl Cabrera let float = RegClassEnv {
267721600caSSaúl Cabrera limit: float,
268721600caSSaúl Cabrera index: 0,
269721600caSSaúl Cabrera };
270721600caSSaúl Cabrera
271721600caSSaúl Cabrera Self {
272721600caSSaúl Cabrera int,
273721600caSSaúl Cabrera float: Some(float),
274721600caSSaúl Cabrera }
275721600caSSaúl Cabrera }
276721600caSSaúl Cabrera
with_absolute_limit(limit: u8) -> Self277721600caSSaúl Cabrera fn with_absolute_limit(limit: u8) -> Self {
278721600caSSaúl Cabrera let int = RegClassEnv { limit, index: 0 };
279721600caSSaúl Cabrera
280721600caSSaúl Cabrera Self { int, float: None }
281721600caSSaúl Cabrera }
282721600caSSaúl Cabrera }
283721600caSSaúl Cabrera
284721600caSSaúl Cabrera impl RegIndexEnv {
next_gpr(&mut self) -> Option<u8>285721600caSSaúl Cabrera fn next_gpr(&mut self) -> Option<u8> {
286721600caSSaúl Cabrera (self.int.index < self.int.limit)
287721600caSSaúl Cabrera .then(|| Self::increment(&mut self.int.index))
288721600caSSaúl Cabrera .flatten()
289721600caSSaúl Cabrera }
290721600caSSaúl Cabrera
next_fpr(&mut self) -> Option<u8>291721600caSSaúl Cabrera fn next_fpr(&mut self) -> Option<u8> {
292721600caSSaúl Cabrera if let Some(f) = self.float.as_mut() {
293721600caSSaúl Cabrera (f.index < f.limit)
294721600caSSaúl Cabrera .then(|| Self::increment(&mut f.index))
295721600caSSaúl Cabrera .flatten()
296721600caSSaúl Cabrera } else {
297721600caSSaúl Cabrera // If a single `RegClassEnv` is used, it means that the count is
298721600caSSaúl Cabrera // absolute, so we default to calling `next_gpr`.
299721600caSSaúl Cabrera self.next_gpr()
300721600caSSaúl Cabrera }
301721600caSSaúl Cabrera }
302721600caSSaúl Cabrera
increment(index: &mut u8) -> Option<u8>303721600caSSaúl Cabrera fn increment(index: &mut u8) -> Option<u8> {
304721600caSSaúl Cabrera let current = *index;
305721600caSSaúl Cabrera match index.checked_add(1) {
306721600caSSaúl Cabrera Some(next) => {
307721600caSSaúl Cabrera *index = next;
308721600caSSaúl Cabrera Some(current)
309721600caSSaúl Cabrera }
310721600caSSaúl Cabrera None => None,
311721600caSSaúl Cabrera }
312721600caSSaúl Cabrera }
313721600caSSaúl Cabrera }
314721600caSSaúl Cabrera
315721600caSSaúl Cabrera #[cfg(test)]
316721600caSSaúl Cabrera mod tests {
317721600caSSaúl Cabrera use super::RegIndexEnv;
318721600caSSaúl Cabrera #[test]
test_get_next_reg_index()319721600caSSaúl Cabrera fn test_get_next_reg_index() {
320721600caSSaúl Cabrera let mut index_env = RegIndexEnv::with_limits_per_class(3, 3);
321721600caSSaúl Cabrera assert_eq!(index_env.next_fpr(), Some(0));
322721600caSSaúl Cabrera assert_eq!(index_env.next_gpr(), Some(0));
323721600caSSaúl Cabrera assert_eq!(index_env.next_fpr(), Some(1));
324721600caSSaúl Cabrera assert_eq!(index_env.next_gpr(), Some(1));
325721600caSSaúl Cabrera assert_eq!(index_env.next_fpr(), Some(2));
326721600caSSaúl Cabrera assert_eq!(index_env.next_gpr(), Some(2));
327721600caSSaúl Cabrera }
328721600caSSaúl Cabrera
329721600caSSaúl Cabrera #[test]
test_reg_index_env_absolute_count()330721600caSSaúl Cabrera fn test_reg_index_env_absolute_count() {
331721600caSSaúl Cabrera let mut e = RegIndexEnv::with_absolute_limit(4);
332721600caSSaúl Cabrera assert!(e.next_gpr() == Some(0));
333721600caSSaúl Cabrera assert!(e.next_fpr() == Some(1));
334721600caSSaúl Cabrera assert!(e.next_gpr() == Some(2));
335721600caSSaúl Cabrera assert!(e.next_fpr() == Some(3));
336721600caSSaúl Cabrera }
337721600caSSaúl Cabrera }
338