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 #[cfg(feature = "unwind")] 10 use crate::machinst::CompiledCode; 11 use crate::machinst::{ 12 CompiledCodeStencil, MachInst, MachTextSectionBuilder, Reg, SigSet, TextSectionBuilder, VCode, 13 compile, 14 }; 15 use crate::result::CodegenResult; 16 use crate::settings as shared_settings; 17 use alloc::string::String; 18 use alloc::{boxed::Box, vec::Vec}; 19 use core::fmt; 20 use cranelift_control::ControlPlane; 21 #[cfg(feature = "unwind")] 22 use target_lexicon::OperatingSystem; 23 use target_lexicon::{Aarch64Architecture, Architecture, Triple}; 24 25 // New backend: 26 mod abi; 27 pub mod inst; 28 mod lower; 29 mod pcc; 30 pub mod settings; 31 32 use self::inst::EmitInfo; 33 34 /// An AArch64 backend. 35 pub struct AArch64Backend { 36 triple: Triple, 37 flags: shared_settings::Flags, 38 isa_flags: aarch64_settings::Flags, 39 } 40 41 impl AArch64Backend { 42 /// Create a new AArch64 backend with the given (shared) flags. 43 pub fn new_with_flags( 44 triple: Triple, 45 flags: shared_settings::Flags, 46 isa_flags: aarch64_settings::Flags, 47 ) -> AArch64Backend { 48 AArch64Backend { 49 triple, 50 flags, 51 isa_flags, 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 value_labels_ranges = emit_result.value_labels_ranges; 82 let buffer = emit_result.buffer; 83 84 if let Some(disasm) = emit_result.disasm.as_ref() { 85 log::debug!("disassembly:\n{disasm}"); 86 } 87 88 Ok(CompiledCodeStencil { 89 buffer, 90 vcode: emit_result.disasm, 91 value_labels_ranges, 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 isa_flags_hash_key(&self) -> IsaFlagsHashKey<'_> { 114 IsaFlagsHashKey(self.isa_flags.hash_key()) 115 } 116 117 fn is_branch_protection_enabled(&self) -> bool { 118 self.isa_flags.use_bti() 119 } 120 121 fn dynamic_vector_bytes(&self, _dyn_ty: Type) -> u32 { 122 16 123 } 124 125 #[cfg(feature = "unwind")] 126 fn emit_unwind_info( 127 &self, 128 result: &CompiledCode, 129 kind: crate::isa::unwind::UnwindInfoKind, 130 ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> { 131 use crate::isa::unwind::UnwindInfo; 132 use crate::isa::unwind::UnwindInfoKind; 133 Ok(match kind { 134 UnwindInfoKind::SystemV => { 135 let mapper = self::inst::unwind::systemv::RegisterMapper; 136 Some(UnwindInfo::SystemV( 137 crate::isa::unwind::systemv::create_unwind_info_from_insts( 138 &result.buffer.unwind_info[..], 139 result.buffer.data().len(), 140 &mapper, 141 )?, 142 )) 143 } 144 UnwindInfoKind::Windows => Some(UnwindInfo::WindowsArm64( 145 crate::isa::unwind::winarm64::create_unwind_info_from_insts( 146 &result.buffer.unwind_info[..], 147 )?, 148 )), 149 _ => None, 150 }) 151 } 152 153 #[cfg(feature = "unwind")] 154 fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { 155 let is_apple_os = match self.triple.operating_system { 156 OperatingSystem::Darwin(_) 157 | OperatingSystem::IOS(_) 158 | OperatingSystem::MacOSX { .. } 159 | OperatingSystem::TvOS(_) => true, 160 _ => false, 161 }; 162 163 if self.isa_flags.sign_return_address() 164 && self.isa_flags.sign_return_address_with_bkey() 165 && !is_apple_os 166 { 167 unimplemented!( 168 "Specifying that the B key is used with pointer authentication instructions in the CIE is not implemented." 169 ); 170 } 171 172 Some(inst::unwind::systemv::create_cie()) 173 } 174 175 fn text_section_builder(&self, num_funcs: usize) -> Box<dyn TextSectionBuilder> { 176 Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs)) 177 } 178 179 #[cfg(feature = "unwind")] 180 fn map_regalloc_reg_to_dwarf(&self, reg: Reg) -> Result<u16, systemv::RegisterMappingError> { 181 inst::unwind::systemv::map_reg(reg).map(|reg| reg.0) 182 } 183 184 fn function_alignment(&self) -> FunctionAlignment { 185 inst::Inst::function_alignment() 186 } 187 188 fn page_size_align_log2(&self) -> u8 { 189 use target_lexicon::*; 190 match self.triple().operating_system { 191 OperatingSystem::MacOSX { .. } 192 | OperatingSystem::Darwin(_) 193 | OperatingSystem::IOS(_) 194 | OperatingSystem::TvOS(_) => { 195 debug_assert_eq!(1 << 14, 0x4000); 196 14 197 } 198 _ => { 199 debug_assert_eq!(1 << 16, 0x10000); 200 16 201 } 202 } 203 } 204 205 #[cfg(feature = "disas")] 206 fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> { 207 use capstone::prelude::*; 208 let mut cs = Capstone::new() 209 .arm64() 210 .mode(arch::arm64::ArchMode::Arm) 211 .detail(true) 212 .build()?; 213 // AArch64 uses inline constants rather than a separate constant pool right now. 214 // Without this option, Capstone will stop disassembling as soon as it sees 215 // an inline constant that is not also a valid instruction. With this option, 216 // Capstone will print a `.byte` directive with the bytes of the inline constant 217 // and continue to the next instruction. 218 cs.set_skipdata(true)?; 219 Ok(cs) 220 } 221 222 fn pretty_print_reg(&self, reg: Reg, _size: u8) -> String { 223 inst::regs::pretty_print_reg(reg) 224 } 225 226 fn has_native_fma(&self) -> bool { 227 true 228 } 229 230 fn has_round(&self) -> bool { 231 true 232 } 233 234 fn has_blendv_lowering(&self, _: Type) -> bool { 235 false 236 } 237 238 fn has_x86_pshufb_lowering(&self) -> bool { 239 false 240 } 241 242 fn has_x86_pmulhrsw_lowering(&self) -> bool { 243 false 244 } 245 246 fn has_x86_pmaddubsw_lowering(&self) -> bool { 247 false 248 } 249 250 fn default_argument_extension(&self) -> ir::ArgumentExtension { 251 // This is copied/carried over from a historical piece of code in 252 // Wasmtime: 253 // 254 // https://github.com/bytecodealliance/wasmtime/blob/a018a5a9addb77d5998021a0150192aa955c71bf/crates/cranelift/src/lib.rs#L366-L374 255 // 256 // Whether or not it is still applicable here is unsure, but it's left 257 // the same as-is for now to reduce the likelihood of problems arising. 258 ir::ArgumentExtension::Uext 259 } 260 } 261 262 impl fmt::Display for AArch64Backend { 263 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 264 f.debug_struct("MachBackend") 265 .field("name", &self.name()) 266 .field("triple", &self.triple()) 267 .field("flags", &format!("{}", self.flags())) 268 .finish() 269 } 270 } 271 272 /// Create a new `isa::Builder`. 273 pub fn isa_builder(triple: Triple) -> IsaBuilder { 274 assert!(triple.architecture == Architecture::Aarch64(Aarch64Architecture::Aarch64)); 275 IsaBuilder { 276 triple, 277 setup: aarch64_settings::builder(), 278 constructor: |triple, shared_flags, builder| { 279 let isa_flags = aarch64_settings::Flags::new(&shared_flags, builder); 280 let backend = AArch64Backend::new_with_flags(triple, shared_flags, isa_flags); 281 Ok(backend.wrapped()) 282 }, 283 } 284 } 285