diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..92ae31e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to qemu-riscv64-static", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceRoot}/hello", + "cwd": "${workspaceRoot}", + "stopAtEntry": true, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "miDebuggerPath": "/usr/bin/gdb-multiarch", + "miDebuggerServerAddress": "localhost:8888", + } + ] +} \ No newline at end of file diff --git a/debug-asmgen.sh b/debug-asmgen.sh new file mode 100755 index 0000000..f1b9ee9 --- /dev/null +++ b/debug-asmgen.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# 사용법 확인 +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + echo "Example: $0 examples/c/unary.c" + exit 1 +fi + +INPUT_C_FILE="$1" +OUTPUT_S_FILE="hello.S" +OUTPUT_BIN="hello" +GDB_PORT=8888 + +# 1단계: Rust 바이너리 빌드 및 어셈블리 생성 +cargo run --features=build-bin -- "$INPUT_C_FILE" > "$OUTPUT_S_FILE" + +# 2단계: 리눅스용 RISC-V 정적 바이너리 생성 +riscv64-linux-gnu-gcc -ggdb -static "$OUTPUT_S_FILE" -o "$OUTPUT_BIN" + +# 3단계: QEMU로 RISC-V 바이너리 실행 (GDB 포트 열림) +qemu-riscv64-static -g "$GDB_PORT" "$OUTPUT_BIN" & +echo "Ready!" +wait diff --git a/src/asmgen/mod.rs b/src/asmgen/mod.rs index 26ba255..cac3b9a 100644 --- a/src/asmgen/mod.rs +++ b/src/asmgen/mod.rs @@ -1,4 +1,5 @@ use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; +use std::ops::Deref; use lang_c::ast; @@ -7,11 +8,17 @@ use crate::opt::opt_utils; use crate::{Translate, asm, ir}; #[derive(Debug)] -pub struct Asmgen {} +pub struct Asmgen { + functions: Vec>, + variables: Vec>, +} impl Default for Asmgen { fn default() -> Self { - todo!() + Asmgen { + functions: Vec::new(), + variables: Vec::new(), + } } } @@ -20,6 +27,470 @@ impl Translate for Asmgen { type Error = (); fn translate(&mut self, source: &ir::TranslationUnit) -> Result { - todo!() + for (name, decl) in &source.decls { + self.translate_decl(name, decl); + } + + // println!("{:?}", self.functions); + + Ok(asm::Asm { + unit: asm::TranslationUnit { + functions: std::mem::replace(&mut self.functions, Vec::new()), + variables: std::mem::replace(&mut self.variables, Vec::new()), + }, + }) + } +} + +struct Context { + insts: Vec, + stack_offsets: HashMap, + stack_allocation: u64, +} + +impl Asmgen { + fn translate_decl(&mut self, name: &String, decl: &ir::Declaration) { + match decl { + ir::Declaration::Variable { dtype, initializer } => todo!(), + ir::Declaration::Function { + signature, + definition, + } => { + if let Some(definition) = definition { + let mut context = self.translate_prologue(definition); + self.translate_block( + name, + definition.bid_init, + &definition.blocks[&definition.bid_init], + &mut context, + ); + let mut blocks = vec![asm::Block::new( + Some(asm::Label(name.clone())), + context.insts, + )]; + + for (bid, block) in &definition.blocks { + if *bid == definition.bid_init { + continue; + } + + context.insts = Vec::new(); + self.translate_block(name, *bid, block, &mut context); + blocks.push(asm::Block::new( + Some(asm::Label::new(name, *bid)), + context.insts, + )); + } + + self.functions.push(asm::Section::new( + vec![ + asm::Directive::Globl(asm::Label(name.clone())), + asm::Directive::Section(asm::SectionType::Text), + asm::Directive::Type( + asm::Label(name.clone()), + asm::SymbolType::Function, + ), + ], + asm::Function::new(blocks), + )) + } + } + } + } + + fn translate_prologue(&mut self, definition: &ir::FunctionDefinition) -> Context { + let mut stack_allocation = 0; + let mut stack_offsets = HashMap::new(); + + for (bid, block) in &definition.blocks { + for (iid, inst) in block.instructions.iter().enumerate() { + let _ = stack_offsets.insert(ir::RegisterId::temp(*bid, iid), stack_allocation); + stack_allocation += ceil_to_multiple_of_16(get_dtype_size(&inst.dtype())); + } + } + + for (aid, dtype) in definition.allocations.iter().enumerate() { + let _ = stack_offsets.insert(ir::RegisterId::local(aid), stack_allocation); + stack_allocation += ceil_to_multiple_of_16(get_dtype_size(dtype)); + } + + stack_allocation += 16; // s0, ra + + let insts = vec![ + asm::Instruction::IType { + instr: asm::IType::ADDI, + rd: asm::Register::Sp, + rs1: asm::Register::Sp, + imm: asm::Immediate::Value(!stack_allocation + 1), + }, + asm::Instruction::SType { + instr: asm::SType::SD, + rs1: asm::Register::Sp, + rs2: asm::Register::S0, + imm: asm::Immediate::Value(stack_allocation - 8), + }, + asm::Instruction::IType { + instr: asm::IType::ADDI, + rd: asm::Register::S0, + rs1: asm::Register::Sp, + imm: asm::Immediate::Value(stack_allocation), + }, + asm::Instruction::SType { + instr: asm::SType::SD, + rs1: asm::Register::S0, + rs2: asm::Register::Ra, + imm: asm::Immediate::Value(!16 + 1), + }, + ]; + Context { + insts, + stack_offsets, + stack_allocation, + } + } + + fn translate_epilogue(&mut self, context: &mut Context) { + context.insts.push(asm::Instruction::IType { + instr: asm::IType::LD, + rd: asm::Register::Ra, + rs1: asm::Register::S0, + imm: asm::Immediate::Value(!16 + 1), + }); + context.insts.push(asm::Instruction::IType { + instr: asm::IType::LD, + rd: asm::Register::S0, + rs1: asm::Register::Sp, + imm: asm::Immediate::Value(context.stack_allocation - 8), + }); + context.insts.push(asm::Instruction::IType { + instr: asm::IType::ADDI, + rd: asm::Register::Sp, + rs1: asm::Register::Sp, + imm: asm::Immediate::Value(context.stack_allocation), + }); + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Ret)); + } + + fn translate_block( + &mut self, + name: &String, + bid: ir::BlockId, + block: &ir::Block, + context: &mut Context, + ) { + for (iid, inst) in block.instructions.iter().enumerate() { + let rid = ir::RegisterId::temp(bid, iid); + match inst.deref() { + ir::Instruction::BinOp { + op, + lhs, + rhs, + dtype, + } => { + let operand_dtype = upgrade_dtype(&lhs.dtype()); + let rs1 = get_lhs_register(&operand_dtype); + let rs2 = get_rhs_register(&operand_dtype); + let rd = get_res_register(dtype); + self.translate_load_operand(lhs, rs1, context); + self.translate_load_operand(rhs, rs2, context); + match op { + ast::BinaryOperator::Multiply => { + context.insts.push(asm::Instruction::RType { + instr: asm::RType::mul(operand_dtype), + rd, + rs1, + rs2: Some(rs2), + }) + } + ast::BinaryOperator::Plus => context.insts.push(asm::Instruction::RType { + instr: asm::RType::add(operand_dtype), + rd, + rs1, + rs2: Some(rs2), + }), + ast::BinaryOperator::Minus => context.insts.push(asm::Instruction::RType { + instr: asm::RType::sub(operand_dtype), + rd, + rs1, + rs2: Some(rs2), + }), + ast::BinaryOperator::Equals => { + context.insts.push(asm::Instruction::RType { + instr: asm::RType::Xor, + rd, + rs1, + rs2: Some(rs2), + }); + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Seqz { rd, rs: rd })) + } + ast::BinaryOperator::Less => { + // TODO: FLoating point + context.insts.push(asm::Instruction::RType { + instr: asm::RType::Slt { + is_signed: operand_dtype.is_int_signed(), + }, + rd, + rs1, + rs2: Some(rs2), + }); + } + ast::BinaryOperator::Greater => { + // TODO: FLoating point + context.insts.push(asm::Instruction::RType { + instr: asm::RType::Slt { + is_signed: operand_dtype.is_int_signed(), + }, + rd, + rs1: rs2, + rs2: Some(rs1), + }); + } + _ => todo!(), + } + self.translate_store_result(&rid, dtype.clone(), rd, context); + } + ir::Instruction::Store { ptr, value } => { + let (ptr_rid, ptr_dtype) = ptr.get_register().unwrap(); + let value_dtype = value.dtype().clone(); + match ptr_rid { + ir::RegisterId::Temp { .. } => { + let rs1 = get_lhs_register(ptr_dtype); + let rs2 = get_rhs_register(&value_dtype); + self.translate_load_operand(ptr, rs1, context); + self.translate_load_operand(value, rs2, context); + context.insts.push(asm::Instruction::SType { + instr: asm::SType::store(value_dtype), + rs1, + rs2, + imm: asm::Immediate::Value(0), + }); + } + ir::RegisterId::Local { aid } => { + let rs2 = get_rhs_register(&value_dtype); + self.translate_load_operand(value, rs2, context); + context.insts.push(asm::Instruction::SType { + instr: asm::SType::store(value_dtype), + rs1: asm::Register::Sp, + rs2, + imm: asm::Immediate::Value(context.stack_offsets[ptr_rid]), + }); + } + _ => todo!(), + } + } + ir::Instruction::Load { ptr } => { + let (ptr_rid, ptr_dtype) = ptr.get_register().unwrap(); + let value_dtype = ptr_dtype.get_pointer_inner().unwrap().clone(); + let rd = get_lhs_register(&value_dtype); + match ptr_rid { + ir::RegisterId::Temp { .. } => { + let rs1 = get_rhs_register(ptr_dtype); + self.translate_load_operand(ptr, rs1, context); + context.insts.push(asm::Instruction::IType { + instr: asm::IType::load(value_dtype.clone()), + rd, + rs1, + imm: asm::Immediate::Value(0), + }); + self.translate_store_result(&rid, value_dtype, rs1, context); + } + ir::RegisterId::Local { aid } => { + let rs1 = get_lhs_register(ptr_dtype); + context.insts.push(asm::Instruction::IType { + instr: asm::IType::load(value_dtype.clone()), + rd, + rs1: asm::Register::Sp, + imm: asm::Immediate::Value(context.stack_offsets[ptr_rid]), + }); + self.translate_store_result(&rid, value_dtype, rs1, context); + } + _ => todo!(), + } + } + ir::Instruction::TypeCast { + value, + target_dtype, + } => { + let rs1 = get_lhs_register(&value.dtype()); + self.translate_load_operand(value, rs1, context); + // TODO: Int <-> Float ... + self.translate_store_result(&rid, target_dtype.clone(), rs1, context); + } + _ => todo!(), + } + } + + match &block.exit { + ir::BlockExit::Jump { arg } => { + // TODO: Phinode + context.insts.push(asm::Instruction::Pseudo(asm::Pseudo::J { + offset: asm::Label::new(name, arg.bid), + })); + } + ir::BlockExit::ConditionalJump { + condition, + arg_then, + arg_else, + } => { + // TODO: Phinode + + let rs1 = get_lhs_register(&condition.dtype()); + self.translate_load_operand(condition, rs1, context); + + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Seqz { + rd: rs1, + rs: rs1, + })); + context.insts.push(asm::Instruction::BType { + instr: asm::BType::Bne, + rs1, + rs2: asm::Register::Zero, + imm: asm::Label::new(name, arg_else.bid), + }); + context.insts.push(asm::Instruction::Pseudo(asm::Pseudo::J { + offset: asm::Label::new(name, arg_then.bid), + })); + } + ir::BlockExit::Switch { + value, + default, + cases, + } => { + // TODO: Phinode + + let rs1 = get_lhs_register(&value.dtype()); + let rs2 = get_rhs_register(&value.dtype()); + self.translate_load_operand(value, rs1, context); + + for (case, arg) in cases { + self.translate_load_operand(&ir::Operand::constant(case.clone()), rs2, context); + context.insts.push(asm::Instruction::BType { + instr: asm::BType::Beq, + rs1, + rs2, + imm: asm::Label::new(name, arg.bid), + }); + } + + context.insts.push(asm::Instruction::Pseudo(asm::Pseudo::J { + offset: asm::Label::new(name, default.bid), + })); + } + ir::BlockExit::Return { value } => { + // TODO + self.translate_load_operand(value, asm::Register::A0, context); + self.translate_epilogue(context); + } + _ => todo!(), + } + } + + fn translate_load_operand( + &mut self, + operand: &ir::Operand, + rd: asm::Register, + context: &mut Context, + ) { + match operand { + ir::Operand::Constant(c) => match c { + ir::Constant::Int { + value, + width, + is_signed, + } => context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Li { + rd, + imm: *value as u64, + })), + _ => todo!(), + }, + ir::Operand::Register { rid, dtype } => context.insts.push(asm::Instruction::IType { + instr: asm::IType::load(dtype.clone()), + rd, + rs1: asm::Register::Sp, + imm: asm::Immediate::Value(context.stack_offsets[rid]), + }), + } + } + + fn translate_store_result( + &mut self, + rid: &ir::RegisterId, + dtype: ir::Dtype, + rs: asm::Register, + context: &mut Context, + ) { + context.insts.push(asm::Instruction::SType { + instr: asm::SType::store(dtype), + rs1: asm::Register::Sp, + rs2: rs, + imm: asm::Immediate::Value(context.stack_offsets[rid]), + }) + } +} + +fn get_lhs_register(dtype: &ir::Dtype) -> asm::Register { + match dtype { + ir::Dtype::Int { .. } | ir::Dtype::Pointer { .. } => asm::Register::T0, + ir::Dtype::Float { .. } => asm::Register::FT0, + _ => todo!(), + } +} + +fn get_rhs_register(dtype: &ir::Dtype) -> asm::Register { + match dtype { + ir::Dtype::Int { .. } | ir::Dtype::Pointer { .. } => asm::Register::T1, + ir::Dtype::Float { .. } => asm::Register::FT1, + _ => todo!(), + } +} + +fn get_res_register(dtype: &ir::Dtype) -> asm::Register { + match dtype { + ir::Dtype::Int { .. } | ir::Dtype::Pointer { .. } => asm::Register::T2, + ir::Dtype::Float { .. } => asm::Register::FT2, + _ => todo!(), + } +} + +fn ceil_to_multiple_of_16(x: u64) -> u64 { + (x + 15) & !15 +} + +fn get_dtype_size(dtype: &ir::Dtype) -> u64 { + ((match dtype { + ir::Dtype::Unit { .. } => 0, + ir::Dtype::Int { width, .. } => std::cmp::max(8, *width), + ir::Dtype::Float { width, .. } => *width, + ir::Dtype::Pointer { .. } => 64, + _ => todo!(), + }) / 8) as u64 +} + +fn upgrade_dtype(dtype: &ir::Dtype) -> ir::Dtype { + match dtype { + ir::Dtype::Int { + width, + is_signed, + is_const, + } => { + if *width < 32 { + ir::Dtype::Int { + width: 32, + is_signed: *is_signed, + is_const: *is_const, + } + } else { + dtype.clone() + } + } + _ => dtype.clone(), } }