1 //! risc-v 64-bit Instruction Set Architecture. 2 3 use crate::dominator_tree::DominatorTree; 4 use crate::ir::{Function, Type}; 5 use crate::isa::riscv64::settings as riscv_settings; 6 use crate::isa::{ 7 Builder as IsaBuilder, FunctionAlignment, IsaFlagsHashKey, OwnedTargetIsa, TargetIsa, 8 }; 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::{self as shared_settings, Flags}; 15 use crate::{CodegenError, ir}; 16 use alloc::string::String; 17 use alloc::{boxed::Box, vec::Vec}; 18 use core::fmt; 19 use cranelift_control::ControlPlane; 20 use target_lexicon::{Architecture, Triple}; 21 mod abi; 22 pub(crate) mod inst; 23 mod lower; 24 mod settings; 25 #[cfg(feature = "unwind")] 26 use crate::isa::unwind::systemv; 27 28 use self::inst::EmitInfo; 29 30 /// An riscv64 backend. 31 pub struct Riscv64Backend { 32 triple: Triple, 33 flags: shared_settings::Flags, 34 isa_flags: riscv_settings::Flags, 35 } 36 37 impl Riscv64Backend { 38 /// Create a new riscv64 backend with the given (shared) flags. 39 pub fn new_with_flags( 40 triple: Triple, 41 flags: shared_settings::Flags, 42 isa_flags: riscv_settings::Flags, 43 ) -> Riscv64Backend { 44 Riscv64Backend { 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(), self.isa_flags.clone()); 60 let sigs = SigSet::new::<abi::Riscv64MachineDeps>(func, &self.flags)?; 61 let abi = abi::Riscv64Callee::new(func, self, &self.isa_flags, &sigs)?; 62 compile::compile::<Riscv64Backend>(func, domtree, self, abi, emit_info, sigs, ctrl_plane) 63 } 64 } 65 66 impl TargetIsa for Riscv64Backend { 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 want_disasm = want_disasm || log::log_enabled!(log::Level::Debug); 77 let emit_result = vcode.emit(®alloc_result, want_disasm, &self.flags, ctrl_plane); 78 let value_labels_ranges = emit_result.value_labels_ranges; 79 let buffer = emit_result.buffer; 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 vcode: emit_result.disasm, 88 value_labels_ranges, 89 bb_starts: emit_result.bb_offsets, 90 bb_edges: emit_result.bb_edges, 91 }) 92 } 93 94 fn name(&self) -> &'static str { 95 "riscv64" 96 } 97 fn dynamic_vector_bytes(&self, _dynamic_ty: ir::Type) -> u32 { 98 16 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 #[cfg(feature = "unwind")] 118 fn emit_unwind_info( 119 &self, 120 result: &CompiledCode, 121 kind: crate::isa::unwind::UnwindInfoKind, 122 ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> { 123 use crate::isa::unwind::UnwindInfo; 124 use crate::isa::unwind::UnwindInfoKind; 125 Ok(match kind { 126 UnwindInfoKind::SystemV => { 127 let mapper = self::inst::unwind::systemv::RegisterMapper; 128 Some(UnwindInfo::SystemV( 129 crate::isa::unwind::systemv::create_unwind_info_from_insts( 130 &result.buffer.unwind_info[..], 131 result.buffer.data().len(), 132 &mapper, 133 )?, 134 )) 135 } 136 UnwindInfoKind::Windows => None, 137 _ => None, 138 }) 139 } 140 141 #[cfg(feature = "unwind")] 142 fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { 143 Some(inst::unwind::systemv::create_cie()) 144 } 145 146 fn text_section_builder(&self, num_funcs: usize) -> Box<dyn TextSectionBuilder> { 147 Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs)) 148 } 149 150 #[cfg(feature = "unwind")] 151 fn map_regalloc_reg_to_dwarf(&self, reg: Reg) -> Result<u16, systemv::RegisterMappingError> { 152 inst::unwind::systemv::map_reg(reg).map(|reg| reg.0) 153 } 154 155 fn function_alignment(&self) -> FunctionAlignment { 156 inst::Inst::function_alignment() 157 } 158 159 fn page_size_align_log2(&self) -> u8 { 160 debug_assert_eq!(1 << 12, 0x1000); 161 12 162 } 163 164 #[cfg(feature = "disas")] 165 fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> { 166 use capstone::prelude::*; 167 let mut cs_builder = Capstone::new().riscv().mode(arch::riscv::ArchMode::RiscV64); 168 169 // Enable C instruction decoding if we have compressed instructions enabled. 170 // 171 // We can't enable this unconditionally because it will cause Capstone to 172 // emit weird instructions and generally mess up when it encounters unknown 173 // instructions, such as any Zba,Zbb,Zbc or Vector instructions. 174 // 175 // This causes the default disassembly to be quite unreadable, so enable 176 // it only when we are actually going to be using them. 177 let uses_compressed = self 178 .isa_flags() 179 .iter() 180 .filter(|f| ["has_zca", "has_zcb", "has_zcd"].contains(&f.name)) 181 .any(|f| f.as_bool().unwrap_or(false)); 182 if uses_compressed { 183 cs_builder = cs_builder.extra_mode([arch::riscv::ArchExtraMode::RiscVC].into_iter()); 184 } 185 186 let mut cs = cs_builder.build()?; 187 188 // Similar to AArch64, RISC-V uses inline constants rather than a separate 189 // constant pool. We want to skip disassembly over inline constants instead 190 // of stopping on invalid bytes. 191 cs.set_skipdata(true)?; 192 Ok(cs) 193 } 194 195 fn pretty_print_reg(&self, reg: Reg, _size: u8) -> String { 196 // TODO-RISC-V: implement proper register pretty-printing. 197 format!("{reg:?}") 198 } 199 200 fn has_native_fma(&self) -> bool { 201 true 202 } 203 204 fn has_round(&self) -> bool { 205 true 206 } 207 208 fn has_blendv_lowering(&self, _: Type) -> bool { 209 false 210 } 211 212 fn has_x86_pshufb_lowering(&self) -> bool { 213 false 214 } 215 216 fn has_x86_pmulhrsw_lowering(&self) -> bool { 217 false 218 } 219 220 fn has_x86_pmaddubsw_lowering(&self) -> bool { 221 false 222 } 223 224 fn default_argument_extension(&self) -> ir::ArgumentExtension { 225 // According to https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf 226 // it says: 227 // 228 // > In RV64, 32-bit types, such as int, are stored in integer 229 // > registers as proper sign extensions of their 32-bit values; that 230 // > is, bits 63..31 are all equal. This restriction holds even for 231 // > unsigned 32-bit types. 232 // 233 // leading to `sext` here. 234 ir::ArgumentExtension::Sext 235 } 236 } 237 238 impl fmt::Display for Riscv64Backend { 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 match triple.architecture { 251 Architecture::Riscv64(..) => {} 252 _ => unreachable!(), 253 } 254 IsaBuilder { 255 triple, 256 setup: riscv_settings::builder(), 257 constructor: isa_constructor, 258 } 259 } 260 261 fn isa_constructor( 262 triple: Triple, 263 shared_flags: Flags, 264 builder: &shared_settings::Builder, 265 ) -> CodegenResult<OwnedTargetIsa> { 266 let isa_flags = riscv_settings::Flags::new(&shared_flags, builder); 267 268 // The RISC-V backend does not work without at least the G extension enabled. 269 // The G extension is simply a combination of the following extensions: 270 // - I: Base Integer Instruction Set 271 // - M: Integer Multiplication and Division 272 // - A: Atomic Instructions 273 // - F: Single-Precision Floating-Point 274 // - D: Double-Precision Floating-Point 275 // - Zicsr: Control and Status Register Instructions 276 // - Zifencei: Instruction-Fetch Fence 277 // 278 // Ensure that those combination of features is enabled. 279 if !(isa_flags.has_m() 280 && isa_flags.has_a() 281 && isa_flags.has_f() 282 && isa_flags.has_d() 283 && isa_flags.has_zicsr() 284 && isa_flags.has_zifencei()) 285 { 286 return Err(CodegenError::Unsupported( 287 "The RISC-V Backend currently requires all the features in the G Extension enabled" 288 .into(), 289 )); 290 } 291 292 let backend = Riscv64Backend::new_with_flags(triple, shared_flags, isa_flags); 293 Ok(backend.wrapped()) 294 } 295