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 target_lexicon::{Aarch64Architecture, Architecture, OperatingSystem, Triple}; 19 20 // New backend: 21 mod abi; 22 pub mod inst; 23 mod lower; 24 pub mod settings; 25 26 use self::inst::EmitInfo; 27 28 /// An AArch64 backend. 29 pub struct AArch64Backend { 30 triple: Triple, 31 flags: shared_settings::Flags, 32 isa_flags: aarch64_settings::Flags, 33 } 34 35 impl AArch64Backend { 36 /// Create a new AArch64 backend with the given (shared) flags. 37 pub fn new_with_flags( 38 triple: Triple, 39 flags: shared_settings::Flags, 40 isa_flags: aarch64_settings::Flags, 41 ) -> AArch64Backend { 42 AArch64Backend { 43 triple, 44 flags, 45 isa_flags, 46 } 47 } 48 49 /// This performs lowering to VCode, register-allocates the code, computes block layout and 50 /// finalizes branches. The result is ready for binary emission. 51 fn compile_vcode( 52 &self, 53 func: &Function, 54 domtree: &DominatorTree, 55 ctrl_plane: &mut ControlPlane, 56 ) -> CodegenResult<(VCode<inst::Inst>, regalloc2::Output)> { 57 let emit_info = EmitInfo::new(self.flags.clone()); 58 let sigs = SigSet::new::<abi::AArch64MachineDeps>(func, &self.flags)?; 59 let abi = abi::AArch64Callee::new(func, self, &self.isa_flags, &sigs)?; 60 compile::compile::<AArch64Backend>(func, domtree, self, abi, emit_info, sigs, ctrl_plane) 61 } 62 } 63 64 impl TargetIsa for AArch64Backend { 65 fn compile_function( 66 &self, 67 func: &Function, 68 domtree: &DominatorTree, 69 want_disasm: bool, 70 ctrl_plane: &mut ControlPlane, 71 ) -> CodegenResult<CompiledCodeStencil> { 72 let (vcode, regalloc_result) = self.compile_vcode(func, domtree, ctrl_plane)?; 73 74 let emit_result = vcode.emit(®alloc_result, want_disasm, &self.flags, ctrl_plane); 75 let frame_size = emit_result.frame_size; 76 let value_labels_ranges = emit_result.value_labels_ranges; 77 let buffer = emit_result.buffer; 78 let sized_stackslot_offsets = emit_result.sized_stackslot_offsets; 79 let dynamic_stackslot_offsets = emit_result.dynamic_stackslot_offsets; 80 81 if let Some(disasm) = emit_result.disasm.as_ref() { 82 log::debug!("disassembly:\n{}", disasm); 83 } 84 85 Ok(CompiledCodeStencil { 86 buffer, 87 frame_size, 88 vcode: emit_result.disasm, 89 value_labels_ranges, 90 sized_stackslot_offsets, 91 dynamic_stackslot_offsets, 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 is_branch_protection_enabled(&self) -> bool { 114 self.isa_flags.use_bti() 115 } 116 117 fn dynamic_vector_bytes(&self, _dyn_ty: Type) -> u32 { 118 16 119 } 120 121 #[cfg(feature = "unwind")] 122 fn emit_unwind_info( 123 &self, 124 result: &CompiledCode, 125 kind: crate::isa::unwind::UnwindInfoKind, 126 ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> { 127 use crate::isa::unwind::UnwindInfo; 128 use crate::isa::unwind::UnwindInfoKind; 129 Ok(match kind { 130 UnwindInfoKind::SystemV => { 131 let mapper = self::inst::unwind::systemv::RegisterMapper; 132 Some(UnwindInfo::SystemV( 133 crate::isa::unwind::systemv::create_unwind_info_from_insts( 134 &result.buffer.unwind_info[..], 135 result.buffer.data().len(), 136 &mapper, 137 )?, 138 )) 139 } 140 UnwindInfoKind::Windows => { 141 // TODO: support Windows unwind info on AArch64 142 None 143 } 144 _ => None, 145 }) 146 } 147 148 #[cfg(feature = "unwind")] 149 fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { 150 let is_apple_os = match self.triple.operating_system { 151 OperatingSystem::Darwin 152 | OperatingSystem::Ios 153 | OperatingSystem::MacOSX { .. } 154 | OperatingSystem::Tvos => true, 155 _ => false, 156 }; 157 158 if self.isa_flags.sign_return_address() 159 && self.isa_flags.sign_return_address_with_bkey() 160 && !is_apple_os 161 { 162 unimplemented!("Specifying that the B key is used with pointer authentication instructions in the CIE is not implemented."); 163 } 164 165 Some(inst::unwind::systemv::create_cie()) 166 } 167 168 fn text_section_builder(&self, num_funcs: usize) -> Box<dyn TextSectionBuilder> { 169 Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs)) 170 } 171 172 #[cfg(feature = "unwind")] 173 fn map_regalloc_reg_to_dwarf(&self, reg: Reg) -> Result<u16, systemv::RegisterMappingError> { 174 inst::unwind::systemv::map_reg(reg).map(|reg| reg.0) 175 } 176 177 fn function_alignment(&self) -> FunctionAlignment { 178 inst::Inst::function_alignment() 179 } 180 181 #[cfg(feature = "disas")] 182 fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> { 183 use capstone::prelude::*; 184 let mut cs = Capstone::new() 185 .arm64() 186 .mode(arch::arm64::ArchMode::Arm) 187 .build()?; 188 // AArch64 uses inline constants rather than a separate constant pool right now. 189 // Without this option, Capstone will stop disassembling as soon as it sees 190 // an inline constant that is not also a valid instruction. With this option, 191 // Capstone will print a `.byte` directive with the bytes of the inline constant 192 // and continue to the next instruction. 193 cs.set_skipdata(true)?; 194 Ok(cs) 195 } 196 197 fn has_native_fma(&self) -> bool { 198 true 199 } 200 201 fn has_x86_blendv_lowering(&self, _: Type) -> bool { 202 false 203 } 204 205 fn has_x86_pshufb_lowering(&self) -> bool { 206 false 207 } 208 209 fn has_x86_pmulhrsw_lowering(&self) -> bool { 210 false 211 } 212 213 fn has_x86_pmaddubsw_lowering(&self) -> bool { 214 false 215 } 216 } 217 218 impl fmt::Display for AArch64Backend { 219 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 220 f.debug_struct("MachBackend") 221 .field("name", &self.name()) 222 .field("triple", &self.triple()) 223 .field("flags", &format!("{}", self.flags())) 224 .finish() 225 } 226 } 227 228 /// Create a new `isa::Builder`. 229 pub fn isa_builder(triple: Triple) -> IsaBuilder { 230 assert!(triple.architecture == Architecture::Aarch64(Aarch64Architecture::Aarch64)); 231 IsaBuilder { 232 triple, 233 setup: aarch64_settings::builder(), 234 constructor: |triple, shared_flags, builder| { 235 let isa_flags = aarch64_settings::Flags::new(&shared_flags, builder); 236 let backend = AArch64Backend::new_with_flags(triple, shared_flags, isa_flags); 237 Ok(backend.wrapped()) 238 }, 239 } 240 } 241