/src/wasmtime/cranelift/codegen/src/isa/riscv64/mod.rs
Line | Count | Source |
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::CompiledCode; |
10 | | use crate::machinst::{ |
11 | | CompiledCodeStencil, MachInst, MachTextSectionBuilder, Reg, SigSet, TextSectionBuilder, VCode, |
12 | | compile, |
13 | | }; |
14 | | use crate::result::CodegenResult; |
15 | | use crate::settings::{self as shared_settings, Flags}; |
16 | | use crate::{CodegenError, ir}; |
17 | | use alloc::string::String; |
18 | | use alloc::{boxed::Box, vec::Vec}; |
19 | | use core::fmt; |
20 | | use cranelift_control::ControlPlane; |
21 | | use target_lexicon::{Architecture, Triple}; |
22 | | mod abi; |
23 | | pub(crate) mod inst; |
24 | | mod lower; |
25 | | mod settings; |
26 | | #[cfg(feature = "unwind")] |
27 | | use crate::isa::unwind::systemv; |
28 | | |
29 | | use self::inst::EmitInfo; |
30 | | |
31 | | /// An riscv64 backend. |
32 | | pub struct Riscv64Backend { |
33 | | triple: Triple, |
34 | | flags: shared_settings::Flags, |
35 | | isa_flags: riscv_settings::Flags, |
36 | | } |
37 | | |
38 | | impl Riscv64Backend { |
39 | 26.5k | fn supports_vector_element_type(&self, ty: Type) -> bool { |
40 | | use ir::types::*; |
41 | | |
42 | 26.5k | match ty { |
43 | 22.7k | I8 | I16 | I32 | I64 => true, |
44 | | // FP16 vector operations require Zvfh. |
45 | 0 | F16 => self.isa_flags.has_f() && self.isa_flags.has_zvfh(), |
46 | 2.61k | F32 => self.isa_flags.has_f(), |
47 | 1.19k | F64 => self.isa_flags.has_d(), |
48 | 0 | _ => false, |
49 | | } |
50 | 26.5k | } |
51 | | |
52 | | /// Create a new riscv64 backend with the given (shared) flags. |
53 | 7.65k | pub fn new_with_flags( |
54 | 7.65k | triple: Triple, |
55 | 7.65k | flags: shared_settings::Flags, |
56 | 7.65k | isa_flags: riscv_settings::Flags, |
57 | 7.65k | ) -> Riscv64Backend { |
58 | 7.65k | Riscv64Backend { |
59 | 7.65k | triple, |
60 | 7.65k | flags, |
61 | 7.65k | isa_flags, |
62 | 7.65k | } |
63 | 7.65k | } |
64 | | |
65 | | /// This performs lowering to VCode, register-allocates the code, computes block layout and |
66 | | /// finalizes branches. The result is ready for binary emission. |
67 | 4.83k | fn compile_vcode( |
68 | 4.83k | &self, |
69 | 4.83k | func: &Function, |
70 | 4.83k | domtree: &DominatorTree, |
71 | 4.83k | regalloc_ctx: &mut regalloc2::Ctx, |
72 | 4.83k | ctrl_plane: &mut ControlPlane, |
73 | 4.83k | ) -> CodegenResult<VCode<inst::Inst>> { |
74 | 4.83k | let emit_info = EmitInfo::new(self.flags.clone(), self.isa_flags.clone()); |
75 | 4.83k | let sigs = SigSet::new::<abi::Riscv64MachineDeps>(func, &self.flags)?; |
76 | 4.83k | let abi = abi::Riscv64Callee::new(func, self, &self.isa_flags, &sigs)?; |
77 | 4.83k | compile::compile::<Riscv64Backend>( |
78 | 4.83k | func, |
79 | 4.83k | domtree, |
80 | 4.83k | regalloc_ctx, |
81 | 4.83k | self, |
82 | 4.83k | abi, |
83 | 4.83k | emit_info, |
84 | 4.83k | sigs, |
85 | 4.83k | ctrl_plane, |
86 | | ) |
87 | 4.83k | } |
88 | | } |
89 | | |
90 | | impl TargetIsa for Riscv64Backend { |
91 | 4.83k | fn compile_function( |
92 | 4.83k | &self, |
93 | 4.83k | func: &Function, |
94 | 4.83k | domtree: &DominatorTree, |
95 | 4.83k | regalloc_ctx: &mut regalloc2::Ctx, |
96 | 4.83k | want_disasm: bool, |
97 | 4.83k | ctrl_plane: &mut ControlPlane, |
98 | 4.83k | ) -> CodegenResult<CompiledCodeStencil> { |
99 | 4.83k | let vcode = self.compile_vcode(func, domtree, regalloc_ctx, ctrl_plane)?; |
100 | | |
101 | 4.83k | let want_disasm = want_disasm || log::log_enabled!(log::Level::Debug); |
102 | 4.83k | let emit_result = vcode.emit(®alloc_ctx.output, want_disasm, &self.flags, ctrl_plane)?; |
103 | 4.83k | let value_labels_ranges = emit_result.value_labels_ranges; |
104 | 4.83k | let buffer = emit_result.buffer; |
105 | | |
106 | 4.83k | if let Some(disasm) = emit_result.disasm.as_ref() { |
107 | 0 | log::debug!("disassembly:\n{disasm}"); |
108 | 4.83k | } |
109 | | |
110 | 4.83k | Ok(CompiledCodeStencil(CompiledCode { |
111 | 4.83k | buffer, |
112 | 4.83k | vcode: emit_result.disasm, |
113 | 4.83k | value_labels_ranges, |
114 | 4.83k | bb_starts: emit_result.bb_offsets, |
115 | 4.83k | bb_edges: emit_result.bb_edges, |
116 | 4.83k | })) |
117 | 4.83k | } |
118 | | |
119 | 4.83k | fn name(&self) -> &'static str { |
120 | 4.83k | "riscv64" |
121 | 4.83k | } |
122 | 0 | fn dynamic_vector_bytes(&self, _dynamic_ty: ir::Type) -> u32 { |
123 | 0 | 16 |
124 | 0 | } |
125 | | |
126 | 865k | fn triple(&self) -> &Triple { |
127 | 865k | &self.triple |
128 | 865k | } |
129 | | |
130 | 68.6k | fn flags(&self) -> &shared_settings::Flags { |
131 | 68.6k | &self.flags |
132 | 68.6k | } |
133 | | |
134 | 119k | fn isa_flags(&self) -> Vec<shared_settings::Value> { |
135 | 119k | self.isa_flags.iter().collect() |
136 | 119k | } |
137 | | |
138 | 4.83k | fn isa_flags_hash_key(&self) -> IsaFlagsHashKey<'_> { |
139 | 4.83k | IsaFlagsHashKey(self.isa_flags.hash_key()) |
140 | 4.83k | } |
141 | | |
142 | | #[cfg(feature = "unwind")] |
143 | 0 | fn emit_unwind_info( |
144 | 0 | &self, |
145 | 0 | result: &CompiledCode, |
146 | 0 | kind: crate::isa::unwind::UnwindInfoKind, |
147 | 0 | ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> { |
148 | | use crate::isa::unwind::UnwindInfo; |
149 | | use crate::isa::unwind::UnwindInfoKind; |
150 | 0 | Ok(match kind { |
151 | | UnwindInfoKind::SystemV => { |
152 | 0 | let mapper = self::inst::unwind::systemv::RegisterMapper; |
153 | | Some(UnwindInfo::SystemV( |
154 | 0 | crate::isa::unwind::systemv::create_unwind_info_from_insts( |
155 | 0 | &result.buffer.unwind_info[..], |
156 | 0 | result.buffer.data().len(), |
157 | 0 | &mapper, |
158 | 0 | )?, |
159 | | )) |
160 | | } |
161 | 0 | UnwindInfoKind::Windows => None, |
162 | 0 | _ => None, |
163 | | }) |
164 | 0 | } |
165 | | |
166 | | #[cfg(feature = "unwind")] |
167 | 0 | fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { |
168 | 0 | Some(inst::unwind::systemv::create_cie()) |
169 | 0 | } |
170 | | |
171 | 0 | fn text_section_builder(&self, num_funcs: usize) -> Box<dyn TextSectionBuilder> { |
172 | 0 | Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs)) |
173 | 0 | } |
174 | | |
175 | | #[cfg(feature = "unwind")] |
176 | 0 | fn map_regalloc_reg_to_dwarf(&self, reg: Reg) -> Result<u16, systemv::RegisterMappingError> { |
177 | 0 | inst::unwind::systemv::map_reg(reg).map(|reg| reg.0) |
178 | 0 | } |
179 | | |
180 | 0 | fn function_alignment(&self) -> FunctionAlignment { |
181 | 0 | inst::Inst::function_alignment() |
182 | 0 | } |
183 | | |
184 | 2.41k | fn page_size_align_log2(&self) -> u8 { |
185 | 2.41k | debug_assert_eq!(1 << 12, 0x1000); |
186 | 2.41k | 12 |
187 | 2.41k | } |
188 | | |
189 | | #[cfg(feature = "disas")] |
190 | 0 | fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> { |
191 | | use capstone::prelude::*; |
192 | 0 | let mut cs_builder = Capstone::new().riscv().mode(arch::riscv::ArchMode::RiscV64); |
193 | | |
194 | | // Enable C instruction decoding if we have compressed instructions enabled. |
195 | | // |
196 | | // We can't enable this unconditionally because it will cause Capstone to |
197 | | // emit weird instructions and generally mess up when it encounters unknown |
198 | | // instructions, such as any Zba,Zbb,Zbc or Vector instructions. |
199 | | // |
200 | | // This causes the default disassembly to be quite unreadable, so enable |
201 | | // it only when we are actually going to be using them. |
202 | 0 | let uses_compressed = self |
203 | 0 | .isa_flags() |
204 | 0 | .iter() |
205 | 0 | .filter(|f| ["has_zca", "has_zcb", "has_zcd"].contains(&f.name)) |
206 | 0 | .any(|f| f.as_bool().unwrap_or(false)); |
207 | 0 | if uses_compressed { |
208 | 0 | cs_builder = cs_builder.extra_mode([arch::riscv::ArchExtraMode::RiscVC].into_iter()); |
209 | 0 | } |
210 | | |
211 | 0 | let mut cs = cs_builder.build()?; |
212 | | |
213 | | // Similar to AArch64, RISC-V uses inline constants rather than a separate |
214 | | // constant pool. We want to skip disassembly over inline constants instead |
215 | | // of stopping on invalid bytes. |
216 | 0 | cs.set_skipdata(true)?; |
217 | 0 | Ok(cs) |
218 | 0 | } |
219 | | |
220 | 0 | fn pretty_print_reg(&self, reg: Reg, _size: u8) -> String { |
221 | | // TODO-RISC-V: implement proper register pretty-printing. |
222 | 0 | format!("{reg:?}") |
223 | 0 | } |
224 | | |
225 | 0 | fn has_native_fma(&self) -> bool { |
226 | 0 | true |
227 | 0 | } |
228 | | |
229 | 0 | fn supports_vector_load_store(&self, ty: Type) -> bool { |
230 | 0 | ty.is_vector() |
231 | 0 | && u64::from(ty.bits()) <= self.isa_flags.min_vec_reg_size() |
232 | 0 | && self.supports_vector_element_type(ty.lane_type()) |
233 | 0 | } |
234 | | |
235 | 0 | fn has_round(&self) -> bool { |
236 | 0 | true |
237 | 0 | } |
238 | | |
239 | 0 | fn has_blendv_lowering(&self, _: Type) -> bool { |
240 | 0 | false |
241 | 0 | } |
242 | | |
243 | 0 | fn has_x86_pshufb_lowering(&self) -> bool { |
244 | 0 | false |
245 | 0 | } |
246 | | |
247 | 0 | fn has_x86_pmulhrsw_lowering(&self) -> bool { |
248 | 0 | false |
249 | 0 | } |
250 | | |
251 | 0 | fn has_x86_pmaddubsw_lowering(&self) -> bool { |
252 | 0 | false |
253 | 0 | } |
254 | | |
255 | 0 | fn default_argument_extension(&self) -> ir::ArgumentExtension { |
256 | | // According to https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf |
257 | | // it says: |
258 | | // |
259 | | // > In RV64, 32-bit types, such as int, are stored in integer |
260 | | // > registers as proper sign extensions of their 32-bit values; that |
261 | | // > is, bits 63..31 are all equal. This restriction holds even for |
262 | | // > unsigned 32-bit types. |
263 | | // |
264 | | // leading to `sext` here. |
265 | 0 | ir::ArgumentExtension::Sext |
266 | 0 | } |
267 | | } |
268 | | |
269 | | impl fmt::Display for Riscv64Backend { |
270 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
271 | 0 | f.debug_struct("MachBackend") |
272 | 0 | .field("name", &self.name()) |
273 | 0 | .field("triple", &self.triple()) |
274 | 0 | .field("flags", &format!("{}", self.flags())) |
275 | 0 | .finish() |
276 | 0 | } |
277 | | } |
278 | | |
279 | | /// Create a new `isa::Builder`. |
280 | 7.65k | pub fn isa_builder(triple: Triple) -> IsaBuilder { |
281 | 7.65k | match triple.architecture { |
282 | 7.65k | Architecture::Riscv64(..) => {} |
283 | 0 | _ => unreachable!(), |
284 | | } |
285 | 7.65k | IsaBuilder { |
286 | 7.65k | triple, |
287 | 7.65k | setup: riscv_settings::builder(), |
288 | 7.65k | constructor: isa_constructor, |
289 | 7.65k | } |
290 | 7.65k | } |
291 | | |
292 | 7.65k | fn isa_constructor( |
293 | 7.65k | triple: Triple, |
294 | 7.65k | shared_flags: Flags, |
295 | 7.65k | builder: &shared_settings::Builder, |
296 | 7.65k | ) -> CodegenResult<OwnedTargetIsa> { |
297 | 7.65k | let isa_flags = riscv_settings::Flags::new(&shared_flags, builder); |
298 | | |
299 | | // The RISC-V backend does not work without at least the G extension enabled. |
300 | | // The G extension is simply a combination of the following extensions: |
301 | | // - I: Base Integer Instruction Set |
302 | | // - M: Integer Multiplication and Division |
303 | | // - A: Atomic Instructions |
304 | | // - F: Single-Precision Floating-Point |
305 | | // - D: Double-Precision Floating-Point |
306 | | // - Zicsr: Control and Status Register Instructions |
307 | | // - Zifencei: Instruction-Fetch Fence |
308 | | // |
309 | | // Ensure that those combination of features is enabled. |
310 | 7.65k | if !(isa_flags.has_m() |
311 | 7.65k | && isa_flags.has_a() |
312 | 7.65k | && isa_flags.has_f() |
313 | 7.65k | && isa_flags.has_d() |
314 | 7.65k | && isa_flags.has_zicsr() |
315 | 7.65k | && isa_flags.has_zifencei()) |
316 | | { |
317 | 0 | return Err(CodegenError::Unsupported( |
318 | 0 | "The RISC-V Backend currently requires all the features in the G Extension enabled" |
319 | 0 | .into(), |
320 | 0 | )); |
321 | 7.65k | } |
322 | | |
323 | 7.65k | let backend = Riscv64Backend::new_with_flags(triple, shared_flags, isa_flags); |
324 | 7.65k | Ok(backend.wrapped()) |
325 | 7.65k | } |