Coverage Report

Created: 2026-10-24 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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(&regalloc_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
}