cranelift_codegen/isa/
mod.rs

1//! Instruction Set Architectures.
2//!
3//! The `isa` module provides a `TargetIsa` trait which provides the behavior specialization needed
4//! by the ISA-independent code generator. The sub-modules of this module provide definitions for
5//! the instruction sets that Cranelift can target. Each sub-module has it's own implementation of
6//! `TargetIsa`.
7//!
8//! # Constructing a `TargetIsa` instance
9//!
10//! The target ISA is built from the following information:
11//!
12//! - The name of the target ISA as a string. Cranelift is a cross-compiler, so the ISA to target
13//!   can be selected dynamically. Individual ISAs can be left out when Cranelift is compiled, so a
14//!   string is used to identify the proper sub-module.
15//! - Values for settings that apply to all ISAs. This is represented by a `settings::Flags`
16//!   instance.
17//! - Values for ISA-specific settings.
18//!
19//! The `isa::lookup()` function is the main entry point which returns an `isa::Builder`
20//! appropriate for the requested ISA:
21//!
22//! ```
23//! # #[macro_use] extern crate target_lexicon;
24//! use cranelift_codegen::isa;
25//! use cranelift_codegen::settings::{self, Configurable};
26//! use std::str::FromStr;
27//! use target_lexicon::Triple;
28//!
29//! let shared_builder = settings::builder();
30//! let shared_flags = settings::Flags::new(shared_builder);
31//!
32//! match isa::lookup(triple!("x86_64")) {
33//!     Err(_) => {
34//!         // The x86_64 target ISA is not available.
35//!     }
36//!     Ok(mut isa_builder) => {
37//!         isa_builder.set("use_popcnt", "on");
38//!         let isa = isa_builder.finish(shared_flags);
39//!     }
40//! }
41//! ```
42//!
43//! The configured target ISA trait object is a `Box<TargetIsa>` which can be used for multiple
44//! concurrent function compilations.
45
46use crate::dominator_tree::DominatorTree;
47pub use crate::isa::call_conv::CallConv;
48
49use crate::flowgraph;
50use crate::ir::{self, Function, Type};
51#[cfg(feature = "unwind")]
52use crate::isa::unwind::{systemv::RegisterMappingError, UnwindInfoKind};
53use crate::machinst::{CompiledCode, CompiledCodeStencil, TextSectionBuilder};
54use crate::settings;
55use crate::settings::Configurable;
56use crate::settings::SetResult;
57use crate::CodegenResult;
58use alloc::{boxed::Box, sync::Arc, vec::Vec};
59use core::fmt;
60use core::fmt::{Debug, Formatter};
61use cranelift_control::ControlPlane;
62use target_lexicon::{triple, Architecture, PointerWidth, Triple};
63
64// This module is made public here for benchmarking purposes. No guarantees are
65// made regarding API stability.
66#[cfg(feature = "x86")]
67pub mod x64;
68
69#[cfg(feature = "arm64")]
70pub mod aarch64;
71
72#[cfg(feature = "riscv64")]
73pub mod riscv64;
74
75#[cfg(feature = "s390x")]
76mod s390x;
77
78pub mod unwind;
79
80mod call_conv;
81
82/// Returns a builder that can create a corresponding `TargetIsa`
83/// or `Err(LookupError::SupportDisabled)` if not enabled.
84macro_rules! isa_builder {
85    ($name: ident, $cfg_terms: tt, $triple: ident) => {{
86        #[cfg $cfg_terms]
87        {
88            Ok($name::isa_builder($triple))
89        }
90        #[cfg(not $cfg_terms)]
91        {
92            Err(LookupError::SupportDisabled)
93        }
94    }};
95}
96
97/// Look for an ISA for the given `triple`.
98/// Return a builder that can create a corresponding `TargetIsa`.
99pub fn lookup(triple: Triple) -> Result<Builder, LookupError> {
100    match triple.architecture {
101        Architecture::X86_64 => {
102            isa_builder!(x64, (feature = "x86"), triple)
103        }
104        Architecture::Aarch64 { .. } => isa_builder!(aarch64, (feature = "arm64"), triple),
105        Architecture::S390x { .. } => isa_builder!(s390x, (feature = "s390x"), triple),
106        Architecture::Riscv64 { .. } => isa_builder!(riscv64, (feature = "riscv64"), triple),
107        _ => Err(LookupError::Unsupported),
108    }
109}
110
111/// The string names of all the supported, but possibly not enabled, architectures. The elements of
112/// this slice are suitable to be passed to the [lookup_by_name] function to obtain the default
113/// configuration for that architecture.
114pub const ALL_ARCHITECTURES: &[&str] = &["x86_64", "aarch64", "s390x", "riscv64"];
115
116/// Look for a supported ISA with the given `name`.
117/// Return a builder that can create a corresponding `TargetIsa`.
118pub fn lookup_by_name(name: &str) -> Result<Builder, LookupError> {
119    lookup(triple!(name))
120}
121
122/// Describes reason for target lookup failure
123#[derive(PartialEq, Eq, Copy, Clone, Debug)]
124pub enum LookupError {
125    /// Support for this target was disabled in the current build.
126    SupportDisabled,
127
128    /// Support for this target has not yet been implemented.
129    Unsupported,
130}
131
132// This is manually implementing Error and Display instead of using thiserror to reduce the amount
133// of dependencies used by Cranelift.
134impl std::error::Error for LookupError {}
135
136impl fmt::Display for LookupError {
137    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
138        match self {
139            LookupError::SupportDisabled => write!(f, "Support for this target is disabled"),
140            LookupError::Unsupported => {
141                write!(f, "Support for this target has not been implemented yet")
142            }
143        }
144    }
145}
146
147/// The type of a polymorphic TargetISA object which is 'static.
148pub type OwnedTargetIsa = Arc<dyn TargetIsa>;
149
150/// Type alias of `IsaBuilder` used for building Cranelift's ISAs.
151pub type Builder = IsaBuilder<CodegenResult<OwnedTargetIsa>>;
152
153/// Builder for a `TargetIsa`.
154/// Modify the ISA-specific settings before creating the `TargetIsa` trait object with `finish`.
155#[derive(Clone)]
156pub struct IsaBuilder<T> {
157    triple: Triple,
158    setup: settings::Builder,
159    constructor: fn(Triple, settings::Flags, &settings::Builder) -> T,
160}
161
162impl<T> IsaBuilder<T> {
163    /// Creates a new ISA-builder from its components, namely the `triple` for
164    /// the ISA, the ISA-specific settings builder, and a final constructor
165    /// function to generate the ISA from its components.
166    pub fn new(
167        triple: Triple,
168        setup: settings::Builder,
169        constructor: fn(Triple, settings::Flags, &settings::Builder) -> T,
170    ) -> Self {
171        IsaBuilder {
172            triple,
173            setup,
174            constructor,
175        }
176    }
177
178    /// Creates a new [Builder] from a [TargetIsa], copying all flags in the
179    /// process.
180    pub fn from_target_isa(target_isa: &dyn TargetIsa) -> Builder {
181        // We should always be able to find the builder for the TargetISA, since presumably we
182        // also generated the previous TargetISA at some point
183        let triple = target_isa.triple().clone();
184        let mut builder = self::lookup(triple).expect("Could not find triple for target ISA");
185
186        // Copy ISA Flags
187        for flag in target_isa.isa_flags() {
188            builder.set(&flag.name, &flag.value_string()).unwrap();
189        }
190
191        builder
192    }
193
194    /// Gets the triple for the builder.
195    pub fn triple(&self) -> &Triple {
196        &self.triple
197    }
198
199    /// Iterates the available settings in the builder.
200    pub fn iter(&self) -> impl Iterator<Item = settings::Setting> {
201        self.setup.iter()
202    }
203
204    /// Combine the ISA-specific settings with the provided
205    /// ISA-independent settings and allocate a fully configured
206    /// `TargetIsa` trait object. May return an error if some of the
207    /// flags are inconsistent or incompatible: for example, some
208    /// platform-independent features, like general SIMD support, may
209    /// need certain ISA extensions to be enabled.
210    pub fn finish(&self, shared_flags: settings::Flags) -> T {
211        (self.constructor)(self.triple.clone(), shared_flags, &self.setup)
212    }
213}
214
215impl<T> settings::Configurable for IsaBuilder<T> {
216    fn set(&mut self, name: &str, value: &str) -> SetResult<()> {
217        self.setup.set(name, value)
218    }
219
220    fn enable(&mut self, name: &str) -> SetResult<()> {
221        self.setup.enable(name)
222    }
223}
224
225/// After determining that an instruction doesn't have an encoding, how should we proceed to
226/// legalize it?
227///
228/// The `Encodings` iterator returns a legalization function to call.
229pub type Legalize =
230    fn(ir::Inst, &mut ir::Function, &mut flowgraph::ControlFlowGraph, &dyn TargetIsa) -> bool;
231
232/// This struct provides information that a frontend may need to know about a target to
233/// produce Cranelift IR for the target.
234#[derive(Clone, Copy, Hash)]
235pub struct TargetFrontendConfig {
236    /// The default calling convention of the target.
237    pub default_call_conv: CallConv,
238
239    /// The pointer width of the target.
240    pub pointer_width: PointerWidth,
241
242    /// The log2 of the target's page size and alignment.
243    ///
244    /// Note that this may be an upper-bound that is larger than necessary for
245    /// some platforms since it may depend on runtime configuration.
246    pub page_size_align_log2: u8,
247}
248
249impl TargetFrontendConfig {
250    /// Get the pointer type of this target.
251    pub fn pointer_type(self) -> ir::Type {
252        ir::Type::int(self.pointer_bits() as u16).unwrap()
253    }
254
255    /// Get the width of pointers on this target, in units of bits.
256    pub fn pointer_bits(self) -> u8 {
257        self.pointer_width.bits()
258    }
259
260    /// Get the width of pointers on this target, in units of bytes.
261    pub fn pointer_bytes(self) -> u8 {
262        self.pointer_width.bytes()
263    }
264}
265
266/// Methods that are specialized to a target ISA.
267///
268/// Implies a Display trait that shows the shared flags, as well as any ISA-specific flags.
269pub trait TargetIsa: fmt::Display + Send + Sync {
270    /// Get the name of this ISA.
271    fn name(&self) -> &'static str;
272
273    /// Get the target triple that was used to make this trait object.
274    fn triple(&self) -> &Triple;
275
276    /// Get the ISA-independent flags that were used to make this trait object.
277    fn flags(&self) -> &settings::Flags;
278
279    /// Get the ISA-dependent flag values that were used to make this trait object.
280    fn isa_flags(&self) -> Vec<settings::Value>;
281
282    /// Get a flag indicating whether branch protection is enabled.
283    fn is_branch_protection_enabled(&self) -> bool {
284        false
285    }
286
287    /// Get the ISA-dependent maximum vector register size, in bytes.
288    fn dynamic_vector_bytes(&self, dynamic_ty: ir::Type) -> u32;
289
290    /// Compile the given function.
291    fn compile_function(
292        &self,
293        func: &Function,
294        domtree: &DominatorTree,
295        want_disasm: bool,
296        ctrl_plane: &mut ControlPlane,
297    ) -> CodegenResult<CompiledCodeStencil>;
298
299    #[cfg(feature = "unwind")]
300    /// Map a regalloc::Reg to its corresponding DWARF register.
301    fn map_regalloc_reg_to_dwarf(
302        &self,
303        _: crate::machinst::Reg,
304    ) -> Result<u16, RegisterMappingError> {
305        Err(RegisterMappingError::UnsupportedArchitecture)
306    }
307
308    /// Creates unwind information for the function.
309    ///
310    /// Returns `None` if there is no unwind information for the function.
311    #[cfg(feature = "unwind")]
312    fn emit_unwind_info(
313        &self,
314        result: &CompiledCode,
315        kind: UnwindInfoKind,
316    ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>>;
317
318    /// Creates a new System V Common Information Entry for the ISA.
319    ///
320    /// Returns `None` if the ISA does not support System V unwind information.
321    #[cfg(feature = "unwind")]
322    fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
323        // By default, an ISA cannot create a System V CIE
324        None
325    }
326
327    /// Returns an object that can be used to build the text section of an
328    /// executable.
329    ///
330    /// This object will internally attempt to handle as many relocations as
331    /// possible using relative calls/jumps/etc between functions.
332    ///
333    /// The `num_labeled_funcs` argument here is the number of functions which
334    /// will be "labeled" or might have calls between them, typically the number
335    /// of defined functions in the object file.
336    fn text_section_builder(&self, num_labeled_funcs: usize) -> Box<dyn TextSectionBuilder>;
337
338    /// Returns the minimum function alignment and the preferred function
339    /// alignment, for performance, required by this ISA.
340    fn function_alignment(&self) -> FunctionAlignment;
341
342    /// The log2 of the target's page size and alignment.
343    ///
344    /// Note that this may be an upper-bound that is larger than necessary for
345    /// some platforms since it may depend on runtime configuration.
346    fn page_size_align_log2(&self) -> u8;
347
348    /// Create a polymorphic TargetIsa from this specific implementation.
349    fn wrapped(self) -> OwnedTargetIsa
350    where
351        Self: Sized + 'static,
352    {
353        Arc::new(self)
354    }
355
356    /// Generate a `Capstone` context for disassembling bytecode for this architecture.
357    #[cfg(feature = "disas")]
358    fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> {
359        Err(capstone::Error::UnsupportedArch)
360    }
361
362    /// Returns whether this ISA has a native fused-multiply-and-add instruction
363    /// for floats.
364    ///
365    /// Currently this only returns false on x86 when some native features are
366    /// not detected.
367    fn has_native_fma(&self) -> bool;
368
369    /// Returns whether the CLIF `x86_blendv` instruction is implemented for
370    /// this ISA for the specified type.
371    fn has_x86_blendv_lowering(&self, ty: Type) -> bool;
372
373    /// Returns whether the CLIF `x86_pshufb` instruction is implemented for
374    /// this ISA.
375    fn has_x86_pshufb_lowering(&self) -> bool;
376
377    /// Returns whether the CLIF `x86_pmulhrsw` instruction is implemented for
378    /// this ISA.
379    fn has_x86_pmulhrsw_lowering(&self) -> bool;
380
381    /// Returns whether the CLIF `x86_pmaddubsw` instruction is implemented for
382    /// this ISA.
383    fn has_x86_pmaddubsw_lowering(&self) -> bool;
384}
385
386/// Function alignment specifications as required by an ISA, returned by
387/// [`TargetIsa::function_alignment`].
388#[derive(Copy, Clone)]
389pub struct FunctionAlignment {
390    /// The minimum alignment required by an ISA, where all functions must be
391    /// aligned to at least this amount.
392    pub minimum: u32,
393    /// A "preferred" alignment which should be used for more
394    /// performance-sensitive situations. This can involve cache-line-aligning
395    /// for example to get more of a small function into fewer cache lines.
396    pub preferred: u32,
397}
398
399/// Methods implemented for free for target ISA!
400impl<'a> dyn TargetIsa + 'a {
401    /// Get the default calling convention of this target.
402    pub fn default_call_conv(&self) -> CallConv {
403        CallConv::triple_default(self.triple())
404    }
405
406    /// Get the endianness of this ISA.
407    pub fn endianness(&self) -> ir::Endianness {
408        match self.triple().endianness().unwrap() {
409            target_lexicon::Endianness::Little => ir::Endianness::Little,
410            target_lexicon::Endianness::Big => ir::Endianness::Big,
411        }
412    }
413
414    /// Returns the minimum symbol alignment for this ISA.
415    pub fn symbol_alignment(&self) -> u64 {
416        match self.triple().architecture {
417            // All symbols need to be aligned to at least 2 on s390x.
418            Architecture::S390x => 2,
419            _ => 1,
420        }
421    }
422
423    /// Get the pointer type of this ISA.
424    pub fn pointer_type(&self) -> ir::Type {
425        ir::Type::int(self.pointer_bits() as u16).unwrap()
426    }
427
428    /// Get the width of pointers on this ISA.
429    pub(crate) fn pointer_width(&self) -> PointerWidth {
430        self.triple().pointer_width().unwrap()
431    }
432
433    /// Get the width of pointers on this ISA, in units of bits.
434    pub fn pointer_bits(&self) -> u8 {
435        self.pointer_width().bits()
436    }
437
438    /// Get the width of pointers on this ISA, in units of bytes.
439    pub fn pointer_bytes(&self) -> u8 {
440        self.pointer_width().bytes()
441    }
442
443    /// Get the information needed by frontends producing Cranelift IR.
444    pub fn frontend_config(&self) -> TargetFrontendConfig {
445        TargetFrontendConfig {
446            default_call_conv: self.default_call_conv(),
447            pointer_width: self.pointer_width(),
448            page_size_align_log2: self.page_size_align_log2(),
449        }
450    }
451}
452
453impl Debug for &dyn TargetIsa {
454    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
455        write!(
456            f,
457            "TargetIsa {{ triple: {:?}, pointer_width: {:?}}}",
458            self.triple(),
459            self.pointer_width()
460        )
461    }
462}