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}