mod abi;
mod frame;
mod regalloc;
mod util;
use abi::RiscVAbi;
use frame::RiscVFrame;
use regalloc::RiscVRegAlloc;
use std::io::Write;
use std::result::Result;
use util::{
emit_int_cmp_op, load_fp_operand_to_register, load_operand_to_register,
load_register_to_register, store_fp_register_to_register, store_register_to_register,
};
use crate::mir::register::RegisterClass;
use crate::mir::{Instruction as MirInst, Module as MirModule, Register};
use crate::mir_codegen::{
Codegen, CodegenError, CodegenOptions, MirCodegenSettings, RegallocStrategy,
capability::CapabilitySet,
};
use lamina_codegen::{Allocation as MirAllocation, GraphColorAllocator, LinearScanAllocator};
use lamina_platform::{TargetArchitecture, TargetOperatingSystem};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use crate::mir_codegen::common::CodegenBase;
pub struct RiscVCodegen<'a> {
base: CodegenBase<'a>,
}
impl<'a> RiscVCodegen<'a> {
pub fn new(target_os: TargetOperatingSystem) -> Self {
Self {
base: CodegenBase::new(target_os),
}
}
pub fn set_module(&mut self, module: &'a MirModule) {
self.base.set_module(module);
}
pub fn drain_output(&mut self) -> Vec<u8> {
self.base.drain_output()
}
pub fn emit_into<W: Write>(
&mut self,
module: &'a MirModule,
writer: &mut W,
codegen_units: usize,
) -> Result<(), crate::error::LaminaError> {
generate_mir_riscv_with_units_and_settings(
module,
writer,
self.base.target_os,
codegen_units,
&MirCodegenSettings::default(),
)
}
}
impl<'a> Codegen for RiscVCodegen<'a> {
const BIN_EXT: &'static str = "o";
const CAN_OUTPUT_ASM: bool = true;
const CAN_OUTPUT_BIN: bool = false;
const SUPPORTED_CODEGEN_OPTS: &'static [CodegenOptions] =
&[CodegenOptions::Debug, CodegenOptions::Release];
const TARGET_OS: TargetOperatingSystem = TargetOperatingSystem::Linux;
const MAX_BIT_WIDTH: u8 = 64;
fn capabilities() -> CapabilitySet {
CapabilitySet::standard_native()
}
fn prepare(
&mut self,
types: &HashMap<String, crate::mir::MirType>,
globals: &HashMap<String, crate::mir::Global>,
funcs: &HashMap<String, crate::mir::Signature>,
codegen_units: usize,
verbose: bool,
options: &[CodegenOptions],
input_name: &str,
) -> Result<(), CodegenError> {
self.base.prepare_base(
types,
globals,
funcs,
codegen_units,
verbose,
options,
input_name,
)
}
fn compile(&mut self) -> Result<(), CodegenError> {
self.base.compile_base()
}
fn finalize(&mut self) -> Result<(), CodegenError> {
self.base.finalize_base()
}
fn emit_asm(&mut self) -> Result<(), CodegenError> {
self.base.emit_asm_base_with_units(
|module, writer, target_os, codegen_units| {
generate_mir_riscv_with_units_and_settings(
module,
writer,
target_os,
codegen_units,
&MirCodegenSettings::default(),
)
},
"RISC-V",
self.base.codegen_units,
)
}
fn emit_bin(&mut self) -> Result<(), CodegenError> {
Err(CodegenError::UnsupportedFeature(
"Binary emission not supported".to_string(),
))
}
}
use crate::mir_codegen::common::{compile_functions_parallel, parallel_codegen_error};
fn compile_single_function_riscv(
func_name: &str,
func: &crate::mir::Function,
target_os: TargetOperatingSystem,
settings: &MirCodegenSettings,
) -> Result<Vec<u8>, CodegenError> {
use std::io::Write;
let mut output = Vec::new();
let abi = RiscVAbi::new(target_os);
let label = abi.mangle_function_name(func_name);
writeln!(output, "{}:", label).map_err(|e| {
CodegenError::InvalidCodegenOptions(format!("IO error: {}", e))
})?;
if settings.emit_asm_debug_lines {
let tag = settings.debug_file_tag.replace('\"', "'");
writeln!(output, " .file 1 \"{}\"", tag).map_err(|e| {
CodegenError::InvalidCodegenOptions(format!("IO error: {}", e))
})?;
}
let mut stack_slots: HashMap<crate::mir::VirtualReg, i32> = HashMap::new();
let mut reg_alloc = RiscVRegAlloc::new(target_os);
if settings.regalloc != RegallocStrategy::Incremental {
let mut def_regs: HashSet<crate::mir::VirtualReg> = HashSet::new();
let mut used_regs: HashSet<crate::mir::VirtualReg> = HashSet::new();
for block in &func.blocks {
for inst in &block.instructions {
if let Some(dst) = inst.def_reg()
&& let Register::Virtual(vreg) = dst
{
def_regs.insert(*vreg);
}
for reg in inst.use_regs() {
if let Register::Virtual(vreg) = reg {
used_regs.insert(*vreg);
}
}
}
}
for vreg in &def_regs {
if !stack_slots.contains_key(vreg) {
let slot_index = stack_slots.len();
stack_slots.insert(*vreg, RiscVFrame::calculate_stack_offset(slot_index));
}
}
for vreg in used_regs {
if !def_regs.contains(&vreg) && !stack_slots.contains_key(&vreg) {
let slot_index = stack_slots.len();
stack_slots.insert(vreg, RiscVFrame::calculate_stack_offset(slot_index));
}
}
let pool = RiscVRegAlloc::gpr_pool_for_global_allocation();
let intervals: Vec<_> = LinearScanAllocator::compute_intervals(func)
.into_iter()
.filter(|i| i.vreg.class == RegisterClass::Gpr)
.collect();
let plan = match settings.regalloc {
RegallocStrategy::LinearScanGlobal => {
LinearScanAllocator::allocate(&intervals, pool.as_slice())
}
RegallocStrategy::GraphColorGlobal => {
GraphColorAllocator::allocate(&intervals, pool.as_slice())
}
RegallocStrategy::Incremental => {
return Err(CodegenError::InvalidCodegenOptions(
"internal: incremental in global branch".to_string(),
));
}
};
reg_alloc = RiscVRegAlloc::from_global_plan(target_os, &plan);
for (v, a) in &plan {
if let MirAllocation::Spill(off) = a {
stack_slots.insert(*v, *off);
}
}
} else {
let mut next_slot = 0usize;
for block in &func.blocks {
for inst in &block.instructions {
if let Some(dst) = inst.def_reg()
&& let Register::Virtual(vreg) = dst
&& !stack_slots.contains_key(vreg)
{
stack_slots.insert(*vreg, RiscVFrame::calculate_stack_offset(next_slot));
next_slot += 1;
}
for reg in inst.use_regs() {
if let Register::Virtual(vreg) = reg
&& !stack_slots.contains_key(vreg)
{
stack_slots.insert(*vreg, RiscVFrame::calculate_stack_offset(next_slot));
next_slot += 1;
}
}
}
}
}
let stack_size = stack_slots.len() * 8;
RiscVFrame::generate_prologue(&mut output, stack_size)
.map_err(|e| CodegenError::InvalidCodegenOptions(e.to_string()))?;
let mut debug_line: u32 = 0;
for block in &func.blocks {
writeln!(output, ".L_{}:", block.label).map_err(|e| {
CodegenError::InvalidCodegenOptions(format!("IO error: {}", e))
})?;
for inst in &block.instructions {
emit_instruction_riscv(
inst,
&mut output,
&mut reg_alloc,
&stack_slots,
target_os,
settings,
&mut debug_line,
)
.map_err(|e| CodegenError::InvalidCodegenOptions(e.to_string()))?;
}
}
Ok(output)
}
pub fn generate_mir_riscv<W: Write>(
module: &MirModule,
writer: &mut W,
target_os: TargetOperatingSystem,
) -> Result<(), crate::error::LaminaError> {
generate_mir_riscv_with_units(module, writer, target_os, 1)
}
pub fn generate_mir_riscv_with_units<W: Write>(
module: &MirModule,
writer: &mut W,
target_os: TargetOperatingSystem,
codegen_units: usize,
) -> Result<(), crate::error::LaminaError> {
generate_mir_riscv_with_units_and_settings(
module,
writer,
target_os,
codegen_units,
&MirCodegenSettings::default(),
)
}
pub fn generate_mir_riscv_with_units_and_settings<W: Write>(
module: &MirModule,
writer: &mut W,
target_os: TargetOperatingSystem,
codegen_units: usize,
settings: &MirCodegenSettings,
) -> Result<(), crate::error::LaminaError> {
crate::mir_codegen::validate_module_call_parameters(module, TargetArchitecture::Riscv64)?;
let abi = RiscVAbi::new(target_os);
writeln!(writer, "{}", abi.get_data_section())?;
writeln!(writer, "{}", abi.get_print_format())?;
writeln!(writer, "{}", abi.get_text_section())?;
writeln!(writer, "{}", abi.get_main_global())?;
for func_name in &module.external_functions {
let label = abi.mangle_function_name(func_name);
writeln!(writer, ".extern {}", label)?;
}
let settings_arc = Arc::new(settings.clone());
let results = compile_functions_parallel(module, target_os, codegen_units, {
let settings_arc = settings_arc.clone();
move |name, func, os| compile_single_function_riscv(name, func, os, settings_arc.as_ref())
})
.map_err(parallel_codegen_error)?;
for result in results {
writer.write_all(&result.assembly)?;
}
Ok(())
}
fn emit_instruction_riscv<W: Write>(
inst: &MirInst,
writer: &mut W,
reg_alloc: &mut RiscVRegAlloc,
stack_slots: &HashMap<crate::mir::VirtualReg, i32>,
target_os: TargetOperatingSystem,
settings: &MirCodegenSettings,
debug_line: &mut u32,
) -> Result<(), crate::error::LaminaError> {
if settings.emit_asm_debug_lines {
*debug_line = debug_line.saturating_add(1);
writeln!(writer, " .loc 1 {} 0", *debug_line)?;
}
match inst {
MirInst::IntBinary {
op,
dst,
lhs,
rhs,
ty: _,
} => {
load_operand_to_register(lhs, writer, reg_alloc, stack_slots, "a0")?;
load_operand_to_register(rhs, writer, reg_alloc, stack_slots, "a1")?;
match op {
crate::mir::IntBinOp::Add => writeln!(writer, " add a0, a0, a1")?,
crate::mir::IntBinOp::Sub => writeln!(writer, " sub a0, a0, a1")?,
crate::mir::IntBinOp::Mul => writeln!(writer, " mul a0, a0, a1")?,
crate::mir::IntBinOp::SDiv => writeln!(writer, " div a0, a0, a1")?,
crate::mir::IntBinOp::UDiv => writeln!(writer, " divu a0, a0, a1")?,
crate::mir::IntBinOp::SRem => writeln!(writer, " rem a0, a0, a1")?,
crate::mir::IntBinOp::URem => writeln!(writer, " remu a0, a0, a1")?,
crate::mir::IntBinOp::And => writeln!(writer, " and a0, a0, a1")?,
crate::mir::IntBinOp::Or => writeln!(writer, " or a0, a0, a1")?,
crate::mir::IntBinOp::Xor => writeln!(writer, " xor a0, a0, a1")?,
crate::mir::IntBinOp::Shl => writeln!(writer, " sll a0, a0, a1")?,
crate::mir::IntBinOp::AShr => writeln!(writer, " sra a0, a0, a1")?,
crate::mir::IntBinOp::LShr => writeln!(writer, " srl a0, a0, a1")?,
}
if let Register::Virtual(vreg) = dst {
store_register_to_register("a0", vreg, writer, reg_alloc, stack_slots)?;
}
}
MirInst::IntCmp {
op,
dst,
lhs,
rhs,
ty: _,
} => {
load_operand_to_register(lhs, writer, reg_alloc, stack_slots, "a0")?;
load_operand_to_register(rhs, writer, reg_alloc, stack_slots, "a1")?;
emit_int_cmp_op(op, writer)?;
if let Register::Virtual(vreg) = dst {
store_register_to_register("a0", vreg, writer, reg_alloc, stack_slots)?;
}
}
MirInst::Call { name, args, ret } => {
let abi = RiscVAbi::new(target_os);
if name == "print" {
if let Some(arg) = args.first() {
match target_os {
TargetOperatingSystem::MacOS => {
writeln!(writer, " la a0, __mir_fmt_int")?;
}
_ => {
writeln!(writer, " la a0, .L_mir_fmt_int")?;
}
}
load_operand_to_register(arg, writer, reg_alloc, stack_slots, "a1")?;
let printf_name = abi.call_stub("print").unwrap_or_else(|| match target_os {
TargetOperatingSystem::MacOS => "_printf".to_string(),
_ => "printf".to_string(),
});
writeln!(writer, " call {}", printf_name)?;
}
} else {
let arg_regs = RiscVAbi::ARG_REGISTERS;
let num_reg_args = args.len().min(arg_regs.len());
let num_stack_args = args.len().saturating_sub(arg_regs.len());
for i in 0..num_reg_args {
let arg = &args[i];
let dest_reg = arg_regs[i];
load_operand_to_register(arg, writer, reg_alloc, stack_slots, dest_reg)?;
}
let stack_space = if num_stack_args > 0 {
((num_stack_args * 8) + 15) & !15
} else {
0
};
if stack_space > 0 {
writeln!(writer, " addi sp, sp, -{}", stack_space)?;
for (i, arg) in args.iter().skip(num_reg_args).enumerate() {
let offset = i * 8;
load_operand_to_register(arg, writer, reg_alloc, stack_slots, "t0")?;
writeln!(writer, " sd t0, {}(sp)", offset)?;
}
}
let target_sym = if let Some(stub) = abi.call_stub(name) {
stub
} else {
abi.mangle_function_name(name)
};
writeln!(writer, " call {}", target_sym)?;
if stack_space > 0 {
writeln!(writer, " addi sp, sp, {}", stack_space)?;
}
}
if let Some(ret_reg) = ret
&& let Register::Virtual(vreg) = ret_reg
{
store_register_to_register("a0", vreg, writer, reg_alloc, stack_slots)?;
}
}
MirInst::TailCall { name, args } => {
let abi = RiscVAbi::new(target_os);
if name == "print" {
return Err(crate::error::LaminaError::CodegenError(
CodegenError::UnsupportedFeature(
"RISC-V: TailCall to print is not supported".to_string(),
),
));
}
let arg_regs = RiscVAbi::ARG_REGISTERS;
let num_reg_args = args.len().min(arg_regs.len());
let num_stack_args = args.len().saturating_sub(arg_regs.len());
for i in 0..num_reg_args {
let arg = &args[i];
let dest_reg = arg_regs[i];
load_operand_to_register(arg, writer, reg_alloc, stack_slots, dest_reg)?;
}
if num_stack_args > 0 {
for (j, arg) in args.iter().skip(num_reg_args).enumerate() {
load_operand_to_register(arg, writer, reg_alloc, stack_slots, "t0")?;
writeln!(writer, " sd t0, {}(fp)", j * 8)?;
}
}
let stack_size = stack_slots.len() * 8;
let target_sym = if let Some(stub) = abi.call_stub(name) {
stub
} else {
abi.mangle_function_name(name)
};
RiscVFrame::generate_tail_epilogue(writer, stack_size, &target_sym).map_err(|e| {
crate::error::LaminaError::CodegenError(
CodegenError::InvalidCodegenOptions(e.to_string()),
)
})?;
}
MirInst::Load {
dst,
addr,
ty,
attrs: _,
} => {
let load_op = match ty {
crate::mir::MirType::Scalar(crate::mir::ScalarType::I1)
| crate::mir::MirType::Scalar(crate::mir::ScalarType::I8) => "lb",
crate::mir::MirType::Scalar(crate::mir::ScalarType::I16) => "lh",
crate::mir::MirType::Scalar(crate::mir::ScalarType::I32) => "lw",
crate::mir::MirType::Scalar(crate::mir::ScalarType::I64)
| crate::mir::MirType::Scalar(crate::mir::ScalarType::Ptr) => "ld",
crate::mir::MirType::Scalar(crate::mir::ScalarType::F32) => "flw",
crate::mir::MirType::Scalar(crate::mir::ScalarType::F64) => "fld",
crate::mir::MirType::Vector(_) => {
return Err(crate::error::LaminaError::CodegenError(
CodegenError::UnsupportedFeature(format!(
"RISC-V load unsupported for type {:?}. \
Vector types are not yet implemented for RISC-V.",
ty
)),
));
}
};
let is_float = matches!(
ty,
crate::mir::MirType::Scalar(crate::mir::ScalarType::F32)
| crate::mir::MirType::Scalar(crate::mir::ScalarType::F64)
);
match addr {
crate::mir::instruction::AddressMode::BaseOffset { base, offset } => {
match base {
Register::Virtual(v) => {
load_register_to_register(v, writer, reg_alloc, stack_slots, "t0")?
}
Register::Physical(p) => writeln!(writer, " mv t0, {}", p.name)?,
}
if is_float {
writeln!(writer, " {} fa0, {}(t0)", load_op, offset)?;
if let Register::Virtual(vreg) = dst {
let is_f32 = matches!(
ty,
crate::mir::MirType::Scalar(crate::mir::ScalarType::F32)
);
store_fp_register_to_register(
"fa0",
vreg,
writer,
reg_alloc,
stack_slots,
is_f32,
)?;
}
} else {
writeln!(writer, " {} a0, {}(t0)", load_op, offset)?;
if let Register::Virtual(vreg) = dst {
store_register_to_register("a0", vreg, writer, reg_alloc, stack_slots)?;
}
}
}
_ => {
return Err(crate::error::LaminaError::CodegenError(
CodegenError::UnsupportedFeature(
"RISC-V load supports only base+offset addressing. \
Complex addressing modes (BaseIndexScale) are not yet implemented."
.to_string(),
),
));
}
}
}
MirInst::Store {
addr,
src,
ty,
attrs: _,
} => {
let store_op = match ty {
crate::mir::MirType::Scalar(crate::mir::ScalarType::I1)
| crate::mir::MirType::Scalar(crate::mir::ScalarType::I8) => "sb",
crate::mir::MirType::Scalar(crate::mir::ScalarType::I16) => "sh",
crate::mir::MirType::Scalar(crate::mir::ScalarType::I32) => "sw",
crate::mir::MirType::Scalar(crate::mir::ScalarType::I64)
| crate::mir::MirType::Scalar(crate::mir::ScalarType::Ptr) => "sd",
crate::mir::MirType::Scalar(crate::mir::ScalarType::F32) => "fsw",
crate::mir::MirType::Scalar(crate::mir::ScalarType::F64) => "fsd",
crate::mir::MirType::Vector(_) => {
return Err(crate::error::LaminaError::CodegenError(
CodegenError::UnsupportedFeature(format!(
"RISC-V store unsupported for type {:?}. \
Vector types are not yet implemented for RISC-V.",
ty
)),
));
}
};
let is_float = matches!(
ty,
crate::mir::MirType::Scalar(crate::mir::ScalarType::F32)
| crate::mir::MirType::Scalar(crate::mir::ScalarType::F64)
);
let is_f32 = matches!(ty, crate::mir::MirType::Scalar(crate::mir::ScalarType::F32));
if is_float {
load_fp_operand_to_register(src, writer, reg_alloc, stack_slots, "fa0", is_f32)?;
} else {
load_operand_to_register(src, writer, reg_alloc, stack_slots, "a0")?;
}
match addr {
crate::mir::instruction::AddressMode::BaseOffset { base, offset } => {
match base {
Register::Virtual(v) => {
load_register_to_register(v, writer, reg_alloc, stack_slots, "t0")?
}
Register::Physical(p) => writeln!(writer, " mv t0, {}", p.name)?,
}
if is_float {
writeln!(writer, " {} fa0, {}(t0)", store_op, offset)?;
} else {
writeln!(writer, " {} a0, {}(t0)", store_op, offset)?;
}
}
_ => {
return Err(crate::error::LaminaError::CodegenError(
CodegenError::UnsupportedFeature(
"RISC-V store supports only base+offset addressing. \
Complex addressing modes (BaseIndexScale) are not yet implemented."
.to_string(),
),
));
}
}
}
MirInst::Ret { value } => {
if let Some(val) = value {
load_operand_to_register(val, writer, reg_alloc, stack_slots, "a0")?;
}
let stack_size = stack_slots.len() * 8;
RiscVFrame::generate_epilogue(writer, stack_size)?;
}
MirInst::Jmp { target } => {
writeln!(writer, " j .L_{}", target)?;
}
MirInst::Br {
cond,
true_target,
false_target,
} => {
if let Register::Virtual(vreg) = cond {
load_register_to_register(vreg, writer, reg_alloc, stack_slots, "t0")?;
writeln!(writer, " bnez t0, .L_{}", true_target)?;
writeln!(writer, " j .L_{}", false_target)?;
}
}
MirInst::FloatBinary {
op,
dst,
lhs,
rhs,
ty,
} => {
let is_f32 = ty.size_bytes() == 4;
let suffix = if is_f32 { "s" } else { "d" };
load_fp_operand_to_register(lhs, writer, reg_alloc, stack_slots, "fa0", is_f32)?;
load_fp_operand_to_register(rhs, writer, reg_alloc, stack_slots, "fa1", is_f32)?;
match op {
crate::mir::FloatBinOp::FAdd => {
writeln!(writer, " fadd.{} fa0, fa0, fa1", suffix)?
}
crate::mir::FloatBinOp::FSub => {
writeln!(writer, " fsub.{} fa0, fa0, fa1", suffix)?
}
crate::mir::FloatBinOp::FMul => {
writeln!(writer, " fmul.{} fa0, fa0, fa1", suffix)?
}
crate::mir::FloatBinOp::FDiv => {
writeln!(writer, " fdiv.{} fa0, fa0, fa1", suffix)?
}
}
if let Register::Virtual(vreg) = dst {
store_fp_register_to_register("fa0", vreg, writer, reg_alloc, stack_slots, is_f32)?;
}
}
MirInst::FloatUnary { op, dst, src, ty } => {
let is_f32 = ty.size_bytes() == 4;
let suffix = if is_f32 { "s" } else { "d" };
load_fp_operand_to_register(src, writer, reg_alloc, stack_slots, "fa0", is_f32)?;
match op {
crate::mir::FloatUnOp::FNeg => writeln!(writer, " fneg.{} fa0, fa0", suffix)?,
crate::mir::FloatUnOp::FSqrt => writeln!(writer, " fsqrt.{} fa0, fa0", suffix)?,
}
if let Register::Virtual(vreg) = dst {
store_fp_register_to_register("fa0", vreg, writer, reg_alloc, stack_slots, is_f32)?;
}
}
MirInst::FloatCmp {
op,
dst,
lhs,
rhs,
ty,
} => {
let is_f32 = ty.size_bytes() == 4;
let suffix = if is_f32 { "s" } else { "d" };
load_fp_operand_to_register(lhs, writer, reg_alloc, stack_slots, "fa0", is_f32)?;
load_fp_operand_to_register(rhs, writer, reg_alloc, stack_slots, "fa1", is_f32)?;
match op {
crate::mir::FloatCmpOp::Eq => writeln!(writer, " feq.{} a0, fa0, fa1", suffix)?,
crate::mir::FloatCmpOp::Ne => {
writeln!(writer, " feq.{} a0, fa0, fa1", suffix)?;
writeln!(writer, " xori a0, a0, 1")?;
}
crate::mir::FloatCmpOp::Lt => writeln!(writer, " flt.{} a0, fa0, fa1", suffix)?,
crate::mir::FloatCmpOp::Le => writeln!(writer, " fle.{} a0, fa0, fa1", suffix)?,
crate::mir::FloatCmpOp::Gt => {
writeln!(writer, " flt.{} a0, fa1, fa0", suffix)?
}
crate::mir::FloatCmpOp::Ge => {
writeln!(writer, " fle.{} a0, fa1, fa0", suffix)?
}
}
if let Register::Virtual(vreg) = dst {
store_register_to_register("a0", vreg, writer, reg_alloc, stack_slots)?;
}
}
MirInst::Select {
dst,
cond,
true_val,
false_val,
ty: _,
} => {
load_operand_to_register(false_val, writer, reg_alloc, stack_slots, "a1")?;
load_operand_to_register(true_val, writer, reg_alloc, stack_slots, "a0")?;
match cond {
Register::Virtual(vreg) => {
load_register_to_register(vreg, writer, reg_alloc, stack_slots, "t0")?;
}
Register::Physical(p) => writeln!(writer, " mv t0, {}", p.name)?,
}
writeln!(writer, " bnez t0, .L_riscv_sel_{:p}", cond)?;
writeln!(writer, " mv a0, a1")?;
writeln!(writer, ".L_riscv_sel_{:p}:", cond)?;
if let Register::Virtual(vreg) = dst {
store_register_to_register("a0", vreg, writer, reg_alloc, stack_slots)?;
}
}
MirInst::Switch {
value,
cases,
default,
} => {
match value {
Register::Virtual(v) => {
load_register_to_register(v, writer, reg_alloc, stack_slots, "a0")?;
}
Register::Physical(p) => writeln!(writer, " mv a0, {}", p.name)?,
}
for (case_val, case_label) in cases {
writeln!(writer, " li t0, {}", case_val)?;
writeln!(writer, " beq a0, t0, .L_{}", case_label)?;
}
writeln!(writer, " j .L_{}", default)?;
}
MirInst::Comment { text } => {
writeln!(writer, " # {}", text)?;
}
MirInst::Unreachable => {
writeln!(writer, " .word 0")?;
}
MirInst::SafePoint | MirInst::StackMap { .. } | MirInst::PatchPoint { .. } => {
}
MirInst::VectorOp { .. } => {
return Err(crate::error::LaminaError::CodegenError(
CodegenError::UnsupportedFeature(
"VectorOp is not yet supported by the RISC-V backend".to_string(),
),
));
}
other => {
return Err(crate::error::LaminaError::CodegenError(
CodegenError::UnsupportedFeature(format!(
"RISC-V backend: instruction not yet supported: {}",
other
)),
));
}
}
Ok(())
}