This commit is contained in:
static
2025-06-14 03:12:08 +00:00
parent f596bf4701
commit f4efab6ebf
3 changed files with 521 additions and 3 deletions

23
.vscode/launch.json vendored Normal file
View File

@@ -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",
}
]
}

24
debug-asmgen.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
# 사용법 확인
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <path-to-c-file>"
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

View File

@@ -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<asm::Section<asm::Function>>,
variables: Vec<asm::Section<asm::Variable>>,
}
impl Default for Asmgen {
fn default() -> Self {
todo!()
Asmgen {
functions: Vec::new(),
variables: Vec::new(),
}
}
}
@@ -20,6 +27,470 @@ impl Translate<ir::TranslationUnit> for Asmgen {
type Error = ();
fn translate(&mut self, source: &ir::TranslationUnit) -> Result<Self::Target, Self::Error> {
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<asm::Instruction>,
stack_offsets: HashMap<ir::RegisterId, u64>,
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(),
}
}