Skip to main content

cranelift_codegen/isa/aarch64/
mod.rs

1//! ARM 64-bit Instruction Set Architecture.
2
3use crate::dominator_tree::DominatorTree;
4use crate::ir::{self, Function, Type};
5use crate::isa::aarch64::settings as aarch64_settings;
6#[cfg(feature = "unwind")]
7use crate::isa::unwind::systemv;
8use crate::isa::{Builder as IsaBuilder, FunctionAlignment, IsaFlagsHashKey, TargetIsa};
9#[cfg(feature = "unwind")]
10use crate::machinst::CompiledCode;
11use crate::machinst::{
12    CompiledCodeStencil, MachInst, MachTextSectionBuilder, Reg, SigSet, TextSectionBuilder, VCode,
13    compile,
14};
15use crate::result::CodegenResult;
16use crate::settings as shared_settings;
17use alloc::string::String;
18use alloc::{boxed::Box, vec::Vec};
19use core::fmt;
20use cranelift_control::ControlPlane;
21#[cfg(feature = "unwind")]
22use target_lexicon::OperatingSystem;
23use target_lexicon::{Aarch64Architecture, Architecture, Triple};
24
25// New backend:
26mod abi;
27pub mod inst;
28mod lower;
29mod pcc;
30pub mod settings;
31
32use self::inst::EmitInfo;
33
34/// An AArch64 backend.
35pub struct AArch64Backend {
36    triple: Triple,
37    flags: shared_settings::Flags,
38    isa_flags: aarch64_settings::Flags,
39}
40
41impl AArch64Backend {
42    /// Create a new AArch64 backend with the given (shared) flags.
43    pub fn new_with_flags(
44        triple: Triple,
45        flags: shared_settings::Flags,
46        isa_flags: aarch64_settings::Flags,
47    ) -> AArch64Backend {
48        AArch64Backend {
49            triple,
50            flags,
51            isa_flags,
52        }
53    }
54
55    /// This performs lowering to VCode, register-allocates the code, computes block layout and
56    /// finalizes branches. The result is ready for binary emission.
57    fn compile_vcode(
58        &self,
59        func: &Function,
60        domtree: &DominatorTree,
61        ctrl_plane: &mut ControlPlane,
62    ) -> CodegenResult<(VCode<inst::Inst>, regalloc2::Output)> {
63        let emit_info = EmitInfo::new(self.flags.clone());
64        let sigs = SigSet::new::<abi::AArch64MachineDeps>(func, &self.flags)?;
65        let abi = abi::AArch64Callee::new(func, self, &self.isa_flags, &sigs)?;
66        compile::compile::<AArch64Backend>(func, domtree, self, abi, emit_info, sigs, ctrl_plane)
67    }
68}
69
70impl TargetIsa for AArch64Backend {
71    fn compile_function(
72        &self,
73        func: &Function,
74        domtree: &DominatorTree,
75        want_disasm: bool,
76        ctrl_plane: &mut ControlPlane,
77    ) -> CodegenResult<CompiledCodeStencil> {
78        let (vcode, regalloc_result) = self.compile_vcode(func, domtree, ctrl_plane)?;
79
80        let emit_result = vcode.emit(&regalloc_result, want_disasm, &self.flags, ctrl_plane);
81        let value_labels_ranges = emit_result.value_labels_ranges;
82        let buffer = emit_result.buffer;
83
84        if let Some(disasm) = emit_result.disasm.as_ref() {
85            log::debug!("disassembly:\n{disasm}");
86        }
87
88        Ok(CompiledCodeStencil {
89            buffer,
90            vcode: emit_result.disasm,
91            value_labels_ranges,
92            bb_starts: emit_result.bb_offsets,
93            bb_edges: emit_result.bb_edges,
94        })
95    }
96
97    fn name(&self) -> &'static str {
98        "aarch64"
99    }
100
101    fn triple(&self) -> &Triple {
102        &self.triple
103    }
104
105    fn flags(&self) -> &shared_settings::Flags {
106        &self.flags
107    }
108
109    fn isa_flags(&self) -> Vec<shared_settings::Value> {
110        self.isa_flags.iter().collect()
111    }
112
113    fn isa_flags_hash_key(&self) -> IsaFlagsHashKey<'_> {
114        IsaFlagsHashKey(self.isa_flags.hash_key())
115    }
116
117    fn is_branch_protection_enabled(&self) -> bool {
118        self.isa_flags.use_bti()
119    }
120
121    fn dynamic_vector_bytes(&self, _dyn_ty: Type) -> u32 {
122        16
123    }
124
125    #[cfg(feature = "unwind")]
126    fn emit_unwind_info(
127        &self,
128        result: &CompiledCode,
129        kind: crate::isa::unwind::UnwindInfoKind,
130    ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
131        use crate::isa::unwind::UnwindInfo;
132        use crate::isa::unwind::UnwindInfoKind;
133        Ok(match kind {
134            UnwindInfoKind::SystemV => {
135                let mapper = self::inst::unwind::systemv::RegisterMapper;
136                Some(UnwindInfo::SystemV(
137                    crate::isa::unwind::systemv::create_unwind_info_from_insts(
138                        &result.buffer.unwind_info[..],
139                        result.buffer.data().len(),
140                        &mapper,
141                    )?,
142                ))
143            }
144            UnwindInfoKind::Windows => Some(UnwindInfo::WindowsArm64(
145                crate::isa::unwind::winarm64::create_unwind_info_from_insts(
146                    &result.buffer.unwind_info[..],
147                )?,
148            )),
149            _ => None,
150        })
151    }
152
153    #[cfg(feature = "unwind")]
154    fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
155        let is_apple_os = match self.triple.operating_system {
156            OperatingSystem::Darwin(_)
157            | OperatingSystem::IOS(_)
158            | OperatingSystem::MacOSX { .. }
159            | OperatingSystem::TvOS(_) => true,
160            _ => false,
161        };
162
163        if self.isa_flags.sign_return_address()
164            && self.isa_flags.sign_return_address_with_bkey()
165            && !is_apple_os
166        {
167            unimplemented!(
168                "Specifying that the B key is used with pointer authentication instructions in the CIE is not implemented."
169            );
170        }
171
172        Some(inst::unwind::systemv::create_cie())
173    }
174
175    fn text_section_builder(&self, num_funcs: usize) -> Box<dyn TextSectionBuilder> {
176        Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs))
177    }
178
179    #[cfg(feature = "unwind")]
180    fn map_regalloc_reg_to_dwarf(&self, reg: Reg) -> Result<u16, systemv::RegisterMappingError> {
181        inst::unwind::systemv::map_reg(reg).map(|reg| reg.0)
182    }
183
184    fn function_alignment(&self) -> FunctionAlignment {
185        inst::Inst::function_alignment()
186    }
187
188    fn page_size_align_log2(&self) -> u8 {
189        use target_lexicon::*;
190        match self.triple().operating_system {
191            OperatingSystem::MacOSX { .. }
192            | OperatingSystem::Darwin(_)
193            | OperatingSystem::IOS(_)
194            | OperatingSystem::TvOS(_) => {
195                debug_assert_eq!(1 << 14, 0x4000);
196                14
197            }
198            _ => {
199                debug_assert_eq!(1 << 16, 0x10000);
200                16
201            }
202        }
203    }
204
205    #[cfg(feature = "disas")]
206    fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> {
207        use capstone::prelude::*;
208        let mut cs = Capstone::new()
209            .arm64()
210            .mode(arch::arm64::ArchMode::Arm)
211            .detail(true)
212            .build()?;
213        // AArch64 uses inline constants rather than a separate constant pool right now.
214        // Without this option, Capstone will stop disassembling as soon as it sees
215        // an inline constant that is not also a valid instruction. With this option,
216        // Capstone will print a `.byte` directive with the bytes of the inline constant
217        // and continue to the next instruction.
218        cs.set_skipdata(true)?;
219        Ok(cs)
220    }
221
222    fn pretty_print_reg(&self, reg: Reg, _size: u8) -> String {
223        inst::regs::pretty_print_reg(reg)
224    }
225
226    fn has_native_fma(&self) -> bool {
227        true
228    }
229
230    fn has_round(&self) -> bool {
231        true
232    }
233
234    fn has_blendv_lowering(&self, _: Type) -> bool {
235        false
236    }
237
238    fn has_x86_pshufb_lowering(&self) -> bool {
239        false
240    }
241
242    fn has_x86_pmulhrsw_lowering(&self) -> bool {
243        false
244    }
245
246    fn has_x86_pmaddubsw_lowering(&self) -> bool {
247        false
248    }
249
250    fn default_argument_extension(&self) -> ir::ArgumentExtension {
251        // This is copied/carried over from a historical piece of code in
252        // Wasmtime:
253        //
254        // https://github.com/bytecodealliance/wasmtime/blob/a018a5a9addb77d5998021a0150192aa955c71bf/crates/cranelift/src/lib.rs#L366-L374
255        //
256        // Whether or not it is still applicable here is unsure, but it's left
257        // the same as-is for now to reduce the likelihood of problems arising.
258        ir::ArgumentExtension::Uext
259    }
260}
261
262impl fmt::Display for AArch64Backend {
263    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264        f.debug_struct("MachBackend")
265            .field("name", &self.name())
266            .field("triple", &self.triple())
267            .field("flags", &format!("{}", self.flags()))
268            .finish()
269    }
270}
271
272/// Create a new `isa::Builder`.
273pub fn isa_builder(triple: Triple) -> IsaBuilder {
274    assert!(triple.architecture == Architecture::Aarch64(Aarch64Architecture::Aarch64));
275    IsaBuilder {
276        triple,
277        setup: aarch64_settings::builder(),
278        constructor: |triple, shared_flags, builder| {
279            let isa_flags = aarch64_settings::Flags::new(&shared_flags, builder);
280            let backend = AArch64Backend::new_with_flags(triple, shared_flags, isa_flags);
281            Ok(backend.wrapped())
282        },
283    }
284}