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