mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-12 21:18:45 +00:00
HW7 (1)
This commit is contained in:
23
.vscode/launch.json
vendored
Normal file
23
.vscode/launch.json
vendored
Normal 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
24
debug-asmgen.sh
Executable 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
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user