1 //! ARM 64-bit Instruction Set Architecture.
2 
3 use crate::dominator_tree::DominatorTree;
4 use crate::ir::{Function, Type};
5 use crate::isa::aarch64::settings as aarch64_settings;
6 #[cfg(feature = "unwind")]
7 use crate::isa::unwind::systemv;
8 use crate::isa::{Builder as IsaBuilder, FunctionAlignment, TargetIsa};
9 use crate::machinst::{
10     compile, CompiledCode, CompiledCodeStencil, MachInst, MachTextSectionBuilder, Reg, SigSet,
11     TextSectionBuilder, VCode,
12 };
13 use crate::result::CodegenResult;
14 use crate::settings as shared_settings;
15 use alloc::{boxed::Box, vec::Vec};
16 use core::fmt;
17 use cranelift_control::ControlPlane;
18 use regalloc2::MachineEnv;
19 use target_lexicon::{Aarch64Architecture, Architecture, OperatingSystem, Triple};
20 
21 // New backend:
22 mod abi;
23 pub mod inst;
24 mod lower;
25 pub mod settings;
26 
27 use inst::create_reg_env;
28 
29 use self::inst::EmitInfo;
30 
31 /// An AArch64 backend.
32 pub struct AArch64Backend {
33     triple: Triple,
34     flags: shared_settings::Flags,
35     isa_flags: aarch64_settings::Flags,
36     machine_env: MachineEnv,
37 }
38 
39 impl AArch64Backend {
40     /// Create a new AArch64 backend with the given (shared) flags.
41     pub fn new_with_flags(
42         triple: Triple,
43         flags: shared_settings::Flags,
44         isa_flags: aarch64_settings::Flags,
45     ) -> AArch64Backend {
46         let machine_env = create_reg_env(&flags);
47         AArch64Backend {
48             triple,
49             flags,
50             isa_flags,
51             machine_env,
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 
70 impl 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 frame_size = emit_result.frame_size;
82         let value_labels_ranges = emit_result.value_labels_ranges;
83         let buffer = emit_result.buffer;
84         let sized_stackslot_offsets = emit_result.sized_stackslot_offsets;
85         let dynamic_stackslot_offsets = emit_result.dynamic_stackslot_offsets;
86 
87         if let Some(disasm) = emit_result.disasm.as_ref() {
88             log::debug!("disassembly:\n{}", disasm);
89         }
90 
91         Ok(CompiledCodeStencil {
92             buffer,
93             frame_size,
94             vcode: emit_result.disasm,
95             value_labels_ranges,
96             sized_stackslot_offsets,
97             dynamic_stackslot_offsets,
98             bb_starts: emit_result.bb_offsets,
99             bb_edges: emit_result.bb_edges,
100         })
101     }
102 
103     fn name(&self) -> &'static str {
104         "aarch64"
105     }
106 
107     fn triple(&self) -> &Triple {
108         &self.triple
109     }
110 
111     fn flags(&self) -> &shared_settings::Flags {
112         &self.flags
113     }
114 
115     fn machine_env(&self) -> &MachineEnv {
116         &self.machine_env
117     }
118 
119     fn isa_flags(&self) -> Vec<shared_settings::Value> {
120         self.isa_flags.iter().collect()
121     }
122 
123     fn is_branch_protection_enabled(&self) -> bool {
124         self.isa_flags.use_bti()
125     }
126 
127     fn dynamic_vector_bytes(&self, _dyn_ty: Type) -> u32 {
128         16
129     }
130 
131     #[cfg(feature = "unwind")]
132     fn emit_unwind_info(
133         &self,
134         result: &CompiledCode,
135         kind: crate::machinst::UnwindInfoKind,
136     ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
137         use crate::isa::unwind::UnwindInfo;
138         use crate::machinst::UnwindInfoKind;
139         Ok(match kind {
140             UnwindInfoKind::SystemV => {
141                 let mapper = self::inst::unwind::systemv::RegisterMapper;
142                 Some(UnwindInfo::SystemV(
143                     crate::isa::unwind::systemv::create_unwind_info_from_insts(
144                         &result.buffer.unwind_info[..],
145                         result.buffer.data().len(),
146                         &mapper,
147                     )?,
148                 ))
149             }
150             UnwindInfoKind::Windows => {
151                 // TODO: support Windows unwind info on AArch64
152                 None
153             }
154             _ => None,
155         })
156     }
157 
158     #[cfg(feature = "unwind")]
159     fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
160         let is_apple_os = match self.triple.operating_system {
161             OperatingSystem::Darwin
162             | OperatingSystem::Ios
163             | OperatingSystem::MacOSX { .. }
164             | OperatingSystem::Tvos => true,
165             _ => false,
166         };
167 
168         if self.isa_flags.sign_return_address()
169             && self.isa_flags.sign_return_address_with_bkey()
170             && !is_apple_os
171         {
172             unimplemented!("Specifying that the B key is used with pointer authentication instructions in the CIE is not implemented.");
173         }
174 
175         Some(inst::unwind::systemv::create_cie())
176     }
177 
178     fn text_section_builder(&self, num_funcs: usize) -> Box<dyn TextSectionBuilder> {
179         Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs))
180     }
181 
182     #[cfg(feature = "unwind")]
183     fn map_regalloc_reg_to_dwarf(&self, reg: Reg) -> Result<u16, systemv::RegisterMappingError> {
184         inst::unwind::systemv::map_reg(reg).map(|reg| reg.0)
185     }
186 
187     fn function_alignment(&self) -> FunctionAlignment {
188         inst::Inst::function_alignment()
189     }
190 
191     #[cfg(feature = "disas")]
192     fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> {
193         use capstone::prelude::*;
194         let mut cs = Capstone::new()
195             .arm64()
196             .mode(arch::arm64::ArchMode::Arm)
197             .build()?;
198         // AArch64 uses inline constants rather than a separate constant pool right now.
199         // Without this option, Capstone will stop disassembling as soon as it sees
200         // an inline constant that is not also a valid instruction. With this option,
201         // Capstone will print a `.byte` directive with the bytes of the inline constant
202         // and continue to the next instruction.
203         cs.set_skipdata(true)?;
204         Ok(cs)
205     }
206 
207     fn has_native_fma(&self) -> bool {
208         true
209     }
210 
211     fn has_x86_blendv_lowering(&self, _: Type) -> bool {
212         false
213     }
214 
215     fn has_x86_pshufb_lowering(&self) -> bool {
216         false
217     }
218 }
219 
220 impl fmt::Display for AArch64Backend {
221     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222         f.debug_struct("MachBackend")
223             .field("name", &self.name())
224             .field("triple", &self.triple())
225             .field("flags", &format!("{}", self.flags()))
226             .finish()
227     }
228 }
229 
230 /// Create a new `isa::Builder`.
231 pub fn isa_builder(triple: Triple) -> IsaBuilder {
232     assert!(triple.architecture == Architecture::Aarch64(Aarch64Architecture::Aarch64));
233     IsaBuilder {
234         triple,
235         setup: aarch64_settings::builder(),
236         constructor: |triple, shared_flags, builder| {
237             let isa_flags = aarch64_settings::Flags::new(&shared_flags, builder);
238             let backend = AArch64Backend::new_with_flags(triple, shared_flags, isa_flags);
239             Ok(backend.wrapped())
240         },
241     }
242 }
243