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(®alloc_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