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