From c2b8d71e160fc64e842af74abe2839e7feebca61 Mon Sep 17 00:00:00 2001 From: static Date: Tue, 17 Jun 2025 01:02:22 +0000 Subject: [PATCH] HW8 (3) --- bench/debug-bench.sh | 10 + src/asmgen/mod.rs | 1737 +++++++++++++++++++++++++++--------------- test-asmgen.sh | 23 + 3 files changed, 1149 insertions(+), 621 deletions(-) create mode 100755 bench/debug-bench.sh create mode 100755 test-asmgen.sh diff --git a/bench/debug-bench.sh b/bench/debug-bench.sh new file mode 100755 index 0000000..3be3d96 --- /dev/null +++ b/bench/debug-bench.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +GDB_PORT=8888 + +make clean +make bench + +qemu-riscv64-static -L /usr/riscv64-linux-gnu -g "$GDB_PORT" bench & +echo "Ready!" +wait diff --git a/src/asmgen/mod.rs b/src/asmgen/mod.rs index edf0652..0ec285d 100644 --- a/src/asmgen/mod.rs +++ b/src/asmgen/mod.rs @@ -1,7 +1,9 @@ -use core::f32; +use core::{f32, num}; use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; +use std::hash::Hash; use std::ops::Deref; +use itertools::Itertools; use lang_c::ast; use crate::ir::HasDtype; @@ -37,6 +39,7 @@ impl Translate for Asmgen { struct InferenceGraph { edges: HashSet<(ir::RegisterId, ir::RegisterId)>, + vertices: HashMap, } impl InferenceGraph { @@ -46,6 +49,8 @@ impl InferenceGraph { let domtree = Domtree::new(code.bid_init, &cfg, &reverse_cfg); let mut lives = HashMap::new(); + let mut first_loop = true; + let mut vertices = HashMap::new(); loop { let mut changed = false; @@ -55,7 +60,21 @@ impl InferenceGraph { let len = block.instructions.len(); let mut uses = HashSet::new(); + if first_loop { + for (i, dtype) in block.phinodes.iter().enumerate() { + let rid = ir::RegisterId::arg(*bid, i); + let _unused = + vertices.insert(rid, (dtype.deref().clone(), asm::Register::Zero)); + } + } + for (i, inst) in block.instructions.iter().enumerate() { + if first_loop { + let rid = ir::RegisterId::temp(*bid, i); + let _unused = + vertices.insert(rid, (inst.dtype().clone(), asm::Register::Zero)); + } + match inst.deref() { ir::Instruction::BinOp { lhs, rhs, .. } => { mark_as_used(lhs, &mut uses); @@ -102,11 +121,17 @@ impl InferenceGraph { } let mut uses = HashSet::new(); + + // for (aid, _) in block.phinodes.iter().enumerate() { + // let _ = uses.insert(ir::RegisterId::arg(*bid, aid)); + // } + match &block.exit { ir::BlockExit::Jump { arg } => { for arg in &arg.args { mark_as_used(arg, &mut uses); } + mark_as_used_phinodes(arg, code, &mut uses); } ir::BlockExit::ConditionalJump { condition, @@ -120,6 +145,8 @@ impl InferenceGraph { for arg in &arg_else.args { mark_as_used(arg, &mut uses); } + mark_as_used_phinodes(arg_then, code, &mut uses); + mark_as_used_phinodes(arg_else, code, &mut uses); } ir::BlockExit::Switch { value, @@ -130,10 +157,12 @@ impl InferenceGraph { for arg in &default.args { mark_as_used(arg, &mut uses); } + mark_as_used_phinodes(default, code, &mut uses); for (_, arg) in cases { for arg in &arg.args { mark_as_used(arg, &mut uses); } + mark_as_used_phinodes(arg, code, &mut uses); } } ir::BlockExit::Return { value } => { @@ -162,11 +191,12 @@ impl InferenceGraph { if !changed { break; } + first_loop = false; } let mut edges = HashSet::new(); - for (_, lives) in &lives { + for lives in lives.values() { for rid1 in lives { for rid2 in lives { if rid1 != rid2 { @@ -176,9 +206,106 @@ impl InferenceGraph { } } - println!("Inference graph: {:?}", edges); + for (bid, block) in &code.blocks { + for (i, _) in block.phinodes.iter().enumerate() { + let rid1 = ir::RegisterId::arg(*bid, i); + for (j, _) in block.phinodes.iter().enumerate() { + if i != j { + let rid2 = ir::RegisterId::arg(*bid, j); + let _ = edges.insert((rid1, rid2)); + } + } + for (j, _) in block.instructions.iter().enumerate() { + let rid2 = ir::RegisterId::temp(*bid, j); + let _ = edges.insert((rid1, rid2)); + let _ = edges.insert((rid2, rid1)); + } + } + } - InferenceGraph { edges } + let mut num_of_edges = HashMap::new(); + for (rid1, _) in &edges { + *num_of_edges.entry(*rid1).or_insert(0) += 1; + } + + let mut vertices_order = vertices + .keys() + .map(|rid| (*rid, num_of_edges.get(rid).cloned().unwrap_or_default())) + .sorted_by(|(_, v1), (_, v2)| v2.cmp(v1)); + for (rid, count) in vertices_order { + if count == 0 { + continue; + } + + let dtype = &vertices[&rid].0; + let neightbors = edges + .iter() + .filter_map(|(r1, r2)| { + if *r1 == rid { + Some(&vertices[r2]) + } else { + None + } + }) + .collect::>(); + let neighbor_registers = neightbors + .iter() + .filter_map(|(_, reg)| { + // TODO: Saved말고 다른 것도 쓰게? + if is_integer(dtype) { + if let asm::Register::Saved(asm::RegisterType::Integer, i) = reg { + return Some(*i); + } + } + if is_float(dtype) { + if let asm::Register::Saved(asm::RegisterType::FloatingPoint, i) = reg { + return Some(*i); + } + } + None + }) + .collect::>(); + if is_integer(dtype) { + let my_i = smallest_missing_integer(&neighbor_registers, 1); // s0는 못 씀 + if my_i >= 12 { + // TODO: Spilling or 레지스터 쪼개기 필요 + } else { + let _unused = vertices.insert( + rid, + ( + dtype.clone(), + asm::Register::Saved(asm::RegisterType::Integer, my_i), + ), + ); + } + } else if is_float(dtype) { + let my_i = smallest_missing_integer(&neighbor_registers, 0); // s0는 못 씀 + if my_i >= 12 { + // TODO: Spilling or 레지스터 쪼개기 필요 + } else { + let _unused = vertices.insert( + rid, + ( + dtype.clone(), + asm::Register::Saved(asm::RegisterType::FloatingPoint, my_i), + ), + ); + } + } else { + // TODO: Spilling or 레지스터 쪼개기 필요 + } + } + + InferenceGraph { edges, vertices } + } + + fn get_register(&self, rid: &ir::RegisterId) -> Option { + let register = self.vertices[rid].1; + if matches!(register, asm::Register::Zero) { + None + } else { + Some(register) + } } } @@ -190,17 +317,36 @@ fn mark_as_used(operand: &ir::Operand, uses: &mut HashSet) { } } +fn mark_as_used_phinodes( + arg: &ir::JumpArg, + code: &ir::FunctionDefinition, + uses: &mut HashSet, +) { + for (aid, _) in code.blocks[&arg.bid].phinodes.iter().enumerate() { + let _ = uses.insert(ir::RegisterId::Arg { bid: arg.bid, aid }); + } +} + fn extend_set(set: &mut HashSet, other: HashSet) -> bool { let len = set.len(); set.extend(other); set.len() != len } +fn smallest_missing_integer(set: &HashSet, start: usize) -> usize { + let mut i = start; + while set.contains(&i) { + i += 1; + } + i +} + struct Context { insts: Vec, stack_offsets: HashMap, stack_allocation: u64, new_blocks: Vec<(asm::Label, Vec)>, + inference_graph: InferenceGraph, } impl Asmgen { @@ -256,10 +402,12 @@ impl Asmgen { definition, } => { if let Some(definition) = definition { - // TEMP let graph = InferenceGraph::new(definition); - let mut context = self.translate_prologue(signature, definition, structs); + // println!("asdf: {:?}", graph.vertices); + + let mut context = + self.translate_prologue(signature, definition, structs, graph); self.translate_block( name, definition.bid_init, @@ -309,18 +457,26 @@ impl Asmgen { signature: &ir::FunctionSignature, definition: &ir::FunctionDefinition, structs: &HashMap>, + inference_graph: InferenceGraph, ) -> Context { let mut stack_allocation = 0; let mut stack_offsets = HashMap::new(); for (bid, block) in &definition.blocks { for (aid, dtype) in block.phinodes.iter().enumerate() { - let _ = stack_offsets.insert(ir::RegisterId::arg(*bid, aid), stack_allocation); - stack_allocation += ceil_to_multiple_of_16(get_dtype_size(dtype, structs)); + let rid = ir::RegisterId::arg(*bid, aid); + if inference_graph.get_register(&rid).is_none() { + let _ = stack_offsets.insert(rid, stack_allocation); + stack_allocation += ceil_to_multiple_of_16(get_dtype_size(dtype, structs)); + } } 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(), structs)); + let rid = ir::RegisterId::temp(*bid, iid); + if inference_graph.get_register(&rid).is_none() { + let _ = stack_offsets.insert(rid, stack_allocation); + stack_allocation += + ceil_to_multiple_of_16(get_dtype_size(&inst.dtype(), structs)); + } } } @@ -329,7 +485,38 @@ impl Asmgen { stack_allocation += ceil_to_multiple_of_16(get_dtype_size(dtype, structs)); } - stack_allocation += 32; // s0, ra, a0, (padding) + stack_allocation += 24; // s0, ra, a0 + + let num_int_regs = inference_graph + .vertices + .values() + .filter_map(|(_, reg)| { + if let asm::Register::Saved(asm::RegisterType::Integer, i) = reg { + Some(i) + } else { + None + } + }) + .collect::>() + .len(); + let num_float_regs = inference_graph + .vertices + .values() + .filter_map(|(_, reg)| { + if let asm::Register::Saved(asm::RegisterType::FloatingPoint, i) = reg { + Some(i) + } else { + None + } + }) + .collect::>() + .len(); + stack_allocation += ((num_int_regs + num_float_regs) * 8) as u64; + + if stack_allocation % 16 != 0 { + // 스택은 16바이트 정렬 + stack_allocation += 16 - (stack_allocation % 16); + } for offset in stack_offsets.values_mut() { *offset = offset.wrapping_sub(stack_allocation); @@ -368,6 +555,27 @@ impl Asmgen { imm: asm::Immediate::Value(!24 + 1), }); + // S1~레지스터 및 FS0~ 레지스터 백업 + for i in 0..num_int_regs { + insts.push(asm::Instruction::SType { + instr: asm::SType::SD, + rs1: asm::Register::S0, + rs2: asm::Register::saved( + asm::RegisterType::Integer, + i + 1, // S0는 이 용도로 쓰지 않음 + ), + imm: asm::Immediate::Value((!(32 + i * 8) + 1) as u64), + }); + } + for i in 0..num_float_regs { + insts.push(asm::Instruction::SType { + instr: asm::SType::store(ir::Dtype::DOUBLE), + rs1: asm::Register::S0, + rs2: asm::Register::saved(asm::RegisterType::FloatingPoint, i), + imm: asm::Immediate::Value((!(32 + num_int_regs * 8 + i * 8) + 1) as u64), + }); + } + let mut num_int_args = 0; let mut num_float_args = 0; @@ -380,33 +588,47 @@ impl Asmgen { let mut large_struct = HashMap::new(); for (i, dtype) in signature.params.iter().enumerate() { - let stack_offset = stack_offsets[&ir::RegisterId::arg(definition.bid_init, i)]; + let rid = ir::RegisterId::arg(definition.bid_init, i); + let rd = inference_graph.get_register(&rid); if is_integer(dtype) { - self.translate_store( - asm::SType::store(dtype.clone()), - asm::Register::S0, - asm::Register::arg(asm::RegisterType::Integer, num_int_args), - stack_offset, - &mut insts, - ); + let rs = asm::Register::arg(asm::RegisterType::Integer, num_int_args); num_int_args += 1; + if let Some(rd) = rd { + insts.push(asm::Instruction::Pseudo(asm::Pseudo::Mv { rd, rs })); + } else { + self.translate_store( + asm::SType::store(dtype.clone()), + asm::Register::S0, + rs, + stack_offsets[&rid], + &mut insts, + ); + } } else if is_float(dtype) { - self.translate_store( - asm::SType::store(dtype.clone()), - asm::Register::S0, - asm::Register::arg(asm::RegisterType::FloatingPoint, num_float_args), - stack_offset, - &mut insts, - ); + let rs = asm::Register::arg(asm::RegisterType::FloatingPoint, num_float_args); num_float_args += 1; + if let Some(rd) = rd { + insts.push(asm::Instruction::Pseudo(asm::Pseudo::Fmv { + data_size: asm::DataSize::try_from(dtype.clone()).unwrap(), + rd, + rs, + })); + } else { + self.translate_store( + asm::SType::store(dtype.clone()), + asm::Register::S0, + rs, + stack_offsets[&rid], + &mut insts, + ); + } } else if let Some(size) = is_struct(dtype, structs) { if size > 16 { let _ = large_struct.insert(i, num_int_args); num_int_args += 1; } else { - let struct_name = dtype.get_struct_name().unwrap().as_ref().unwrap().clone(); - let struct_dtype = structs[&struct_name].clone().unwrap(); - let fields = struct_dtype.get_struct_fields().unwrap().as_ref().unwrap(); + let stack_offset = stack_offsets[&rid]; + let (struct_dtype, fields) = get_struct_dtype(dtype, structs); let mut is_packing = false; let mut packing_start_offset = 0; @@ -416,39 +638,37 @@ impl Asmgen { let (offset, _) = struct_dtype .get_offset_struct_field(field_dtype.name().unwrap(), structs) .unwrap(); - let field_size = get_dtype_size(&field_dtype, structs); - if is_integer(&field_dtype) { + let field_size = get_dtype_size(field_dtype, structs); + if is_integer(field_dtype) { if !is_packing { is_packing = true; packing_start_offset = offset; packing_size = field_size; + } else if offset == packing_start_offset + (packing_size as usize) + && packing_size + field_size <= 8 + { + packing_size += field_size; } else { - if offset == packing_start_offset + (packing_size as usize) - && packing_size + field_size <= 8 - { - packing_size += field_size; - } else { - self.translate_addi( - asm::Register::T5, - asm::Register::S0, - stack_offset, - &mut insts, - ); - insts.push(asm::Instruction::SType { - instr: asm::SType::SD, - rs1: asm::Register::T5, - rs2: asm::Register::arg( - asm::RegisterType::Integer, - num_int_args, - ), - imm: asm::Immediate::Value(packing_start_offset as u64), - }); - num_int_args += 1; - packing_start_offset = offset; - packing_size = field_size; - } + self.translate_addi( + asm::Register::T5, + asm::Register::S0, + stack_offset, + &mut insts, + ); + insts.push(asm::Instruction::SType { + instr: asm::SType::SD, + rs1: asm::Register::T5, + rs2: asm::Register::arg( + asm::RegisterType::Integer, + num_int_args, + ), + imm: asm::Immediate::Value(packing_start_offset as u64), + }); + num_int_args += 1; + packing_start_offset = offset; + packing_size = field_size; } - } else if is_float(&field_dtype) { + } else if is_float(field_dtype) { if is_packing { self.translate_addi( asm::Register::T5, @@ -529,8 +749,8 @@ impl Asmgen { }); for (i, dtype) in signature.params.iter().enumerate() { - let stack_offset = stack_offsets[&ir::RegisterId::arg(definition.bid_init, i)]; if let Some(size) = is_struct(dtype, structs) { + let stack_offset = stack_offsets[&ir::RegisterId::arg(definition.bid_init, i)]; if size > 16 { let reg_index = large_struct[&i]; @@ -589,10 +809,55 @@ impl Asmgen { stack_offsets, stack_allocation, new_blocks: Vec::new(), + inference_graph, } } fn translate_epilogue(&mut self, context: &mut Context) { + // S1~레지스터 및 FS0~ 레지스터 복원 + let num_int_regs = context + .inference_graph + .vertices + .values() + .filter_map(|(_, reg)| { + if let asm::Register::Saved(asm::RegisterType::Integer, i) = reg { + Some(*i) + } else { + None + } + }) + .collect::>() + .len(); + let num_float_regs = context + .inference_graph + .vertices + .values() + .filter_map(|(_, reg)| { + if let asm::Register::Saved(asm::RegisterType::FloatingPoint, i) = reg { + Some(*i) + } else { + None + } + }) + .collect::>() + .len(); + for i in 0..num_float_regs { + context.insts.push(asm::Instruction::IType { + instr: asm::IType::load(ir::Dtype::DOUBLE), + rd: asm::Register::saved(asm::RegisterType::FloatingPoint, i), + rs1: asm::Register::S0, + imm: asm::Immediate::Value((!(32 + num_int_regs * 8 + i * 8) + 1) as u64), + }); + } + for i in 0..num_int_regs { + context.insts.push(asm::Instruction::IType { + instr: asm::IType::LD, + rs1: asm::Register::S0, + rd: asm::Register::saved(asm::RegisterType::Integer, i + 1), // S0는 이 용도로 쓰지 않음 + imm: asm::Immediate::Value((!(32 + i * 8) + 1) as u64), + }); + } + context.insts.push(asm::Instruction::IType { instr: asm::IType::LD, rd: asm::Register::Ra, @@ -627,19 +892,25 @@ impl Asmgen { ) { for (iid, inst) in block.instructions.iter().enumerate() { let rid = ir::RegisterId::temp(bid, iid); + let rd = context.inference_graph.get_register(&rid); + let is_spilled = rd.is_none(); + match inst.deref() { + ir::Instruction::Nop => (), 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); + let org_operand_dtype = lhs.dtype(); + let operand_dtype = upgrade_dtype(&org_operand_dtype); + let rs1 = + self.translate_load_operand(lhs, get_lhs_register(&operand_dtype), context); + let rs2 = + self.translate_load_operand(rhs, get_rhs_register(&operand_dtype), context); + let rd = rd.unwrap_or(get_res_register(dtype)); + match op { ast::BinaryOperator::Multiply => { context.insts.push(asm::Instruction::RType { @@ -651,7 +922,7 @@ impl Asmgen { rd, rs1, rs2: Some(rs2), - }) + }); } ast::BinaryOperator::Divide => { context.insts.push(asm::Instruction::RType { @@ -664,16 +935,16 @@ impl Asmgen { rd, rs1, rs2: Some(rs2), - }) + }); } ast::BinaryOperator::Modulo => { - let is_signed: bool = operand_dtype.is_int_signed(); + let is_signed = operand_dtype.is_int_signed(); context.insts.push(asm::Instruction::RType { instr: asm::RType::rem(operand_dtype, is_signed), rd, rs1, rs2: Some(rs2), - }) + }); } ast::BinaryOperator::Plus => context.insts.push(asm::Instruction::RType { instr: if is_integer(&operand_dtype) { @@ -704,21 +975,76 @@ impl Asmgen { }); } ast::BinaryOperator::ShiftRight => { - if operand_dtype.is_int_signed() { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::sra(operand_dtype), - rd, - rs1, - rs2: Some(rs2), - }); + context.insts.push(asm::Instruction::RType { + instr: if operand_dtype.is_int_signed() { + asm::RType::sra(operand_dtype) + } else { + asm::RType::srl(operand_dtype) + }, + rd, + rs1, + rs2: Some(rs2), + }); + } + ast::BinaryOperator::Less => context.insts.push(asm::Instruction::RType { + instr: if is_integer(&operand_dtype) { + asm::RType::Slt { + is_signed: operand_dtype.is_int_signed(), + } } else { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::srl(operand_dtype), - rd, - rs1, - rs2: Some(rs2), - }); - } + asm::RType::flt(operand_dtype) + }, + rd, + rs1, + rs2: Some(rs2), + }), + ast::BinaryOperator::Greater => { + context.insts.push(asm::Instruction::RType { + instr: if is_integer(&operand_dtype) { + asm::RType::Slt { + is_signed: operand_dtype.is_int_signed(), + } + } else { + asm::RType::flt(operand_dtype) + }, + rd, + rs1: rs2, + rs2: Some(rs1), + }); + } + ast::BinaryOperator::LessOrEqual => { + context.insts.push(asm::Instruction::RType { + instr: if is_integer(&operand_dtype) { + asm::RType::Slt { + is_signed: operand_dtype.is_int_signed(), + } + } else { + asm::RType::flt(operand_dtype) + }, + rd, + rs1: rs2, + rs2: Some(rs1), + }); + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Seqz { rd, rs: rd })) + } + ast::BinaryOperator::GreaterOrEqual => { + context.insts.push(asm::Instruction::RType { + instr: if is_integer(&operand_dtype) { + asm::RType::Slt { + is_signed: operand_dtype.is_int_signed(), + } + } else { + asm::RType::flt(operand_dtype) + }, + rd, + rs1, + rs2: Some(rs2), + }); + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Seqz { rd, rs: rd })) } ast::BinaryOperator::Equals => { context.insts.push(asm::Instruction::RType { @@ -742,103 +1068,13 @@ impl Asmgen { .insts .push(asm::Instruction::Pseudo(asm::Pseudo::Snez { rd, rs: rd })); } - ast::BinaryOperator::Less => { - if is_integer(&operand_dtype) { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::Slt { - is_signed: operand_dtype.is_int_signed(), - }, - rd, - rs1, - rs2: Some(rs2), - }); - } else { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::flt(operand_dtype), - rd, - rs1, - rs2: Some(rs2), - }); - } - } - ast::BinaryOperator::Greater => { - if is_integer(&operand_dtype) { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::Slt { - is_signed: operand_dtype.is_int_signed(), - }, - rd, - rs1: rs2, - rs2: Some(rs1), - }); - } else { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::flt(operand_dtype), - rd, - rs1: rs2, - rs2: Some(rs1), - }); - } - } - ast::BinaryOperator::LessOrEqual => { - if is_integer(&operand_dtype) { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::Slt { - is_signed: operand_dtype.is_int_signed(), - }, - rd, - rs1: rs2, - rs2: Some(rs1), - }); - } else { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::flt(operand_dtype), - rd, - rs1: rs2, - rs2: Some(rs1), - }); - } - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::Seqz { rd, rs: rd })) - } - ast::BinaryOperator::GreaterOrEqual => { - if is_integer(&operand_dtype) { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::Slt { - is_signed: operand_dtype.is_int_signed(), - }, - rd, - rs1, - rs2: Some(rs2), - }); - } else { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::flt(operand_dtype), - rd, - rs1, - rs2: Some(rs2), - }); - } - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::Seqz { rd, rs: rd })) - } ast::BinaryOperator::BitwiseAnd => { context.insts.push(asm::Instruction::RType { instr: asm::RType::And, rd, rs1, rs2: Some(rs2), - }) - } - ast::BinaryOperator::BitwiseOr => { - context.insts.push(asm::Instruction::RType { - instr: asm::RType::Or, - rd, - rs1, - rs2: Some(rs2), - }) + }); } ast::BinaryOperator::BitwiseXor => { context.insts.push(asm::Instruction::RType { @@ -846,62 +1082,96 @@ impl Asmgen { rd, rs1, rs2: Some(rs2), - }) + }); } - _ => { - println!("{:?}", op); - todo!() + ast::BinaryOperator::BitwiseOr => { + context.insts.push(asm::Instruction::RType { + instr: asm::RType::Or, + rd, + rs1, + rs2: Some(rs2), + }); } + _ => unreachable!(), + } + + // if !matches!( + // op, + // ast::BinaryOperator::Equals + // | ast::BinaryOperator::NotEquals + // | ast::BinaryOperator::Less + // | ast::BinaryOperator::Greater + // | ast::BinaryOperator::LessOrEqual + // | ast::BinaryOperator::GreaterOrEqual + // ) { + // // 하위 바이트만 + // if is_integer(&org_operand_dtype) + // && get_dtype_size(&org_operand_dtype, structs) < 4 + // { + // context.insts.push(asm::Instruction::IType { + // instr: asm::IType::Andi, + // rd, + // rs1: rd, + // imm: asm::Immediate::Value( + // (1 << (get_dtype_size(&org_operand_dtype, structs) * 8)) - 1, + // ), + // }); + // } + // } + + if is_spilled { + self.translate_store_result(&rid, dtype.clone(), rd, context); } - self.translate_store_result(&rid, dtype.clone(), rd, context); } ir::Instruction::UnaryOp { op, operand, dtype } => { - let rs1 = get_lhs_register(&operand.dtype()); - let rd = get_res_register(dtype); - self.translate_load_operand(operand, rs1, context); + let operand_dtype = operand.dtype(); + let rs1 = self.translate_load_operand( + operand, + get_lhs_register(&operand_dtype), + context, + ); + let rd = rd.unwrap_or(get_res_register(dtype)); + match op { - ast::UnaryOperator::Minus => { - if is_integer(&operand.dtype()) { - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::neg( - operand.dtype(), - rd, - rs1, - ))); + ast::UnaryOperator::Minus => context.insts.push(asm::Instruction::Pseudo( + if is_integer(&operand_dtype) { + asm::Pseudo::neg(operand_dtype, rd, rs1) } else { - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::fneg( - operand.dtype(), - rd, - rs1, - ))); - } - } + asm::Pseudo::fneg(operand_dtype, rd, rs1) + }, + )), ast::UnaryOperator::Negate => context .insts .push(asm::Instruction::Pseudo(asm::Pseudo::Seqz { rd, rs: rs1 })), - _ => todo!(), + _ => unreachable!(), + } + + if is_spilled { + self.translate_store_result(&rid, dtype.clone(), rd, context); } - self.translate_store_result(&rid, dtype.clone(), rd, context); } ir::Instruction::Store { ptr, value } => { - let value_dtype = value.dtype().clone(); + let value_dtype = value.dtype(); match ptr { - ir::Operand::Constant(ir::Constant::GlobalVariable { name, dtype }) => { - let rd = get_lhs_register(dtype); + ir::Operand::Constant(ir::Constant::GlobalVariable { + name: ptr_name, + .. + }) => { + // TODO: 구조체 context .insts .push(asm::Instruction::Pseudo(asm::Pseudo::La { rd: asm::Register::T0, - symbol: asm::Label(name.clone()), + symbol: asm::Label(ptr_name.clone()), })); - let rs2 = get_rhs_register(&value_dtype); - self.translate_load_operand(value, rs2, context); + let rs2 = self.translate_load_operand( + value, + get_rhs_register(&value_dtype), + context, + ); context.insts.push(asm::Instruction::SType { instr: asm::SType::store(value_dtype), - rs1: rd, + rs1: asm::Register::T0, rs2, imm: asm::Immediate::Value(0), }); @@ -910,46 +1180,6 @@ impl Asmgen { rid: ptr_rid, dtype: ptr_dtype, } => match ptr_rid { - ir::RegisterId::Temp { .. } | ir::RegisterId::Arg { .. } => { - if let Some(size) = is_struct(&value_dtype, structs) { - let rs1 = get_lhs_register(ptr_dtype); - self.translate_load_operand(ptr, rs1, context); - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { - rd: asm::Register::A0, - rs: rs1, - })); - self.translate_addi( - asm::Register::A1, - asm::Register::S0, - context.stack_offsets[value.get_register().unwrap().0], - &mut context.insts, - ); - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::Li { - rd: asm::Register::A2, - imm: size, - })); - context.insts.push(asm::Instruction::Pseudo( - asm::Pseudo::Call { - offset: asm::Label(String::from("memcpy")), - }, - )); - } else { - 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 } => { if let Some(size) = is_struct(&value_dtype, structs) { self.translate_addi( @@ -976,8 +1206,11 @@ impl Asmgen { }, )); } else { - let rs2 = get_rhs_register(&value_dtype); - self.translate_load_operand(value, rs2, context); + let rs2 = self.translate_load_operand( + value, + get_rhs_register(&value_dtype), + context, + ); self.translate_store( asm::SType::store(value_dtype), asm::Register::S0, @@ -987,50 +1220,25 @@ impl Asmgen { ); } } - _ => todo!(), - }, - _ => todo!(), - } - } - ir::Instruction::Load { ptr } => match ptr { - ir::Operand::Constant(ir::Constant::GlobalVariable { name, dtype }) => { - let rd = get_lhs_register(dtype); - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::La { - rd: asm::Register::T0, - symbol: asm::Label(name.clone()), - })); - context.insts.push(asm::Instruction::IType { - instr: asm::IType::load(dtype.clone()), - rd, - rs1: asm::Register::T0, - imm: asm::Immediate::Value(0), - }); - self.translate_store_result(&rid, dtype.clone(), rd, context); - } - ir::Operand::Register { - rid: ptr_rid, - dtype: ptr_dtype, - } => { - let value_dtype = ptr_dtype.get_pointer_inner().unwrap().clone(); - if let Some(size) = is_struct(&value_dtype, structs) { - match ptr_rid { - ir::RegisterId::Temp { .. } | ir::RegisterId::Arg { .. } => { - let rs1 = get_rhs_register(ptr_dtype); - self.translate_load_operand(ptr, rs1, context); - self.translate_addi( - asm::Register::A0, - asm::Register::S0, - context.stack_offsets[&rid], - &mut context.insts, - ); + _ => { + if let Some(size) = is_struct(&value_dtype, structs) { + let rs1 = self.translate_load_operand( + ptr, + get_lhs_register(ptr_dtype), + context, + ); // TODO: 최적화? (MV와 합칠 수 있을듯) context .insts .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { - rd: asm::Register::A1, + rd: asm::Register::A0, rs: rs1, })); + self.translate_addi( + asm::Register::A1, + asm::Register::S0, + context.stack_offsets[value.get_register().unwrap().0], + &mut context.insts, + ); context .insts .push(asm::Instruction::Pseudo(asm::Pseudo::Li { @@ -1042,8 +1250,60 @@ impl Asmgen { offset: asm::Label(String::from("memcpy")), }, )); + } else { + let rs1 = self.translate_load_operand( + ptr, + get_lhs_register(ptr_dtype), + context, + ); + let rs2 = self.translate_load_operand( + value, + get_rhs_register(&value_dtype), + context, + ); + context.insts.push(asm::Instruction::SType { + instr: asm::SType::store(value_dtype), + rs1, + rs2, + imm: asm::Immediate::Value(0), + }); } - ir::RegisterId::Local { aid } => { + } + }, + _ => unreachable!(), + } + } + ir::Instruction::Load { ptr } => match ptr { + ir::Operand::Constant(ir::Constant::GlobalVariable { + name: ptr_name, + dtype: value_dtype, + }) => { + // TODO: 구조체 + let rd = rd.unwrap_or(get_res_register(value_dtype)); + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::La { + rd: asm::Register::T0, + symbol: asm::Label(ptr_name.clone()), + })); + context.insts.push(asm::Instruction::IType { + instr: asm::IType::load(value_dtype.clone()), + rd, + rs1: asm::Register::T0, + imm: asm::Immediate::Value(0), + }); + if is_spilled { + self.translate_store_result(&rid, value_dtype.clone(), rd, context); + } + } + ir::Operand::Register { + rid: ptr_rid, + dtype: ptr_dtype, + } => { + let value_dtype = ptr_dtype.get_pointer_inner().unwrap(); + match ptr_rid { + ir::RegisterId::Local { aid } => { + if let Some(size) = is_struct(value_dtype, structs) { self.translate_addi( asm::Register::A0, asm::Register::S0, @@ -1067,25 +1327,8 @@ impl Asmgen { offset: asm::Label(String::from("memcpy")), }, )); - } - _ => todo!(), - } - } else { - let rd = get_lhs_register(&value_dtype); - match ptr_rid { - ir::RegisterId::Temp { .. } | ir::RegisterId::Arg { .. } => { - 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, rd, context); - } - ir::RegisterId::Local { aid } => { - let rs1 = get_lhs_register(ptr_dtype); + } else { + let rd = rd.unwrap_or(get_res_register(value_dtype)); self.translate_load( asm::IType::load(value_dtype.clone()), rd, @@ -1093,13 +1336,72 @@ impl Asmgen { context.stack_offsets[ptr_rid], &mut context.insts, ); - self.translate_store_result(&rid, value_dtype, rd, context); + if is_spilled { + self.translate_store_result( + &rid, + value_dtype.clone(), + rd, + context, + ); + } + } + } + _ => { + if let Some(size) = is_struct(value_dtype, structs) { + let rs1 = self.translate_load_operand( + ptr, + get_lhs_register(ptr_dtype), + context, + ); // TODO: 최적화? (MV와 합칠 수 있을듯) + self.translate_addi( + asm::Register::A0, + asm::Register::S0, + context.stack_offsets[&rid], + &mut context.insts, + ); + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { + rd: asm::Register::A1, + rs: rs1, + })); + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Li { + rd: asm::Register::A2, + imm: size, + })); + context.insts.push(asm::Instruction::Pseudo( + asm::Pseudo::Call { + offset: asm::Label(String::from("memcpy")), + }, + )); + } else { + let rs1 = self.translate_load_operand( + ptr, + get_lhs_register(ptr_dtype), + context, + ); + let rd = rd.unwrap_or(get_res_register(value_dtype)); + context.insts.push(asm::Instruction::IType { + instr: asm::IType::load(value_dtype.clone()), + rd, + rs1, + imm: asm::Immediate::Value(0), + }); + if is_spilled { + self.translate_store_result( + &rid, + value_dtype.clone(), + rd, + context, + ); + } } - _ => todo!(), } } } - _ => todo!(), + _ => unreachable!(), }, ir::Instruction::Call { callee, @@ -1176,24 +1478,33 @@ impl Asmgen { } } + // 인수 전달 for (i, arg) in args.iter().enumerate() { let dtype = arg.dtype(); if is_integer(&dtype) { - self.translate_load_operand( - arg, - asm::Register::arg(asm::RegisterType::Integer, num_int_args), - context, - ); + let rd = asm::Register::arg(asm::RegisterType::Integer, num_int_args); + let rs = self.translate_load_operand(arg, rd, context); + if rd != rs { + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { rd, rs })); + } num_int_args += 1; } else if is_float(&dtype) { - self.translate_load_operand( - arg, - asm::Register::arg( - asm::RegisterType::FloatingPoint, - num_float_args, - ), - context, + let rd = asm::Register::arg( + asm::RegisterType::FloatingPoint, + num_float_args, ); + let rs = self.translate_load_operand(arg, rd, context); + if rd != rs { + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Fmv { + data_size: asm::DataSize::try_from(dtype.clone()).unwrap(), + rd, + rs, + })); + } num_float_args += 1; } else if let Some(size) = is_struct(&dtype, structs) { if size > 16 { @@ -1220,11 +1531,7 @@ impl Asmgen { _ => todo!(), } - let struct_name = - dtype.get_struct_name().unwrap().as_ref().unwrap().clone(); - let struct_dtype = structs[&struct_name].clone().unwrap(); - let fields = - struct_dtype.get_struct_fields().unwrap().as_ref().unwrap(); + let (struct_dtype, fields) = get_struct_dtype(&dtype, structs); let mut is_packing = false; let mut packing_start_offset = 0; @@ -1237,8 +1544,8 @@ impl Asmgen { structs, ) .unwrap(); - let field_size = get_dtype_size(&field_dtype, structs); - if is_integer(&field_dtype) { + let field_size = get_dtype_size(field_dtype, structs); + if is_integer(field_dtype) { if !is_packing { is_packing = true; packing_start_offset = offset; @@ -1267,7 +1574,7 @@ impl Asmgen { packing_size = field_size; } } - } else if is_float(&field_dtype) { + } else if is_float(field_dtype) { if is_packing { context.insts.push(asm::Instruction::IType { instr: asm::IType::LD, @@ -1334,33 +1641,52 @@ impl Asmgen { .insts .push(asm::Instruction::Pseudo(asm::Pseudo::Call { offset: asm::Label(callee.get_global_variable_name().unwrap()), - })); + })) } ir::Operand::Register { rid, .. } => { - self.translate_load_operand(callee, asm::Register::T0, context); + let rs = + self.translate_load_operand(callee, asm::Register::T0, context); context .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::Jalr { - rs: asm::Register::T0, - })); + .push(asm::Instruction::Pseudo(asm::Pseudo::Jalr { rs })); } } // TODO if is_integer(return_type) { - self.translate_store_result( - &rid, - return_type.clone(), - asm::Register::A0, - context, - ); + if let Some(rd) = context.inference_graph.get_register(&rid) { + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { + rd, + rs: asm::Register::A0, + })); + } else { + self.translate_store_result( + &rid, + return_type.clone(), + asm::Register::A0, + context, + ); + } } else if is_float(return_type) { - self.translate_store_result( - &rid, - return_type.clone(), - asm::Register::FA0, - context, - ); + if let Some(rd) = context.inference_graph.get_register(&rid) { + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Fmv { + data_size: asm::DataSize::try_from(return_type.clone()) + .unwrap(), + rd, + rs: asm::Register::FA0, + })); + } else { + self.translate_store_result( + &rid, + return_type.clone(), + asm::Register::FA0, + context, + ); + } } else if let Some(size) = is_struct(return_type, structs) { if size > 16 { self.translate_addi( @@ -1387,15 +1713,7 @@ impl Asmgen { offset: asm::Label(String::from("memcpy")), })); } else { - let struct_name = return_type - .get_struct_name() - .unwrap() - .as_ref() - .unwrap() - .clone(); - let struct_dtype = structs[&struct_name].clone().unwrap(); - let fields = - struct_dtype.get_struct_fields().unwrap().as_ref().unwrap(); + let (struct_dtype, fields) = get_struct_dtype(return_type, structs); let mut num_int_fields = 0; let mut num_float_fields = 0; @@ -1403,7 +1721,7 @@ impl Asmgen { let (offset, _) = struct_dtype .get_offset_struct_field(field_dtype.name().unwrap(), structs) .unwrap(); - if is_integer(&field_dtype) { + if is_integer(field_dtype) { self.translate_addi( asm::Register::T5, asm::Register::S0, @@ -1420,7 +1738,7 @@ impl Asmgen { imm: asm::Immediate::Value(0), }); num_int_fields += 1; - } else if is_float(&field_dtype) { + } else if is_float(field_dtype) { self.translate_addi( asm::Register::T5, asm::Register::S0, @@ -1457,29 +1775,152 @@ impl Asmgen { value, target_dtype, } => { - let rs1 = get_lhs_register(&value.dtype()); - self.translate_load_operand(value, rs1, context); - - match (value.dtype(), target_dtype) { - (ir::Dtype::Int { .. }, ir::Dtype::Int { .. }) - | (ir::Dtype::Int { .. }, ir::Dtype::Pointer { .. }) => { - self.translate_store_result(&rid, target_dtype.clone(), rs1, context) + let value_dtype = value.dtype(); + let rs1 = + self.translate_load_operand(value, get_lhs_register(&value_dtype), context); + let rd = rd.unwrap_or(get_res_register(target_dtype)); + match (&value_dtype, target_dtype) { + ( + ir::Dtype::Int { + width, is_signed, .. + }, + ir::Dtype::Int { + width: target_width, + is_signed: target_is_signed, + .. + }, + ) => { + if target_width <= width { + let rs2 = get_rhs_register(&value_dtype); + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Li { + rd: rs2, + imm: match target_width { + 1 => 1, + 8 => 0xFF, + 16 => 0xFFFF, + 32 => 0xFFFFFFFF, + 64 => 0xFFFFFFFFFFFFFFFF, + _ => unreachable!(), + }, + })); + context.insts.push(asm::Instruction::RType { + instr: asm::RType::And, + rd, + rs1, + rs2: Some(rs2), + }); + if *target_is_signed { + match target_width { + 8 => { + context.insts.push(asm::Instruction::IType { + instr: asm::IType::slli(ir::Dtype::LONGLONG), + rd, + rs1: rd, + imm: asm::Immediate::Value(56), + }); + context.insts.push(asm::Instruction::IType { + instr: asm::IType::srai(ir::Dtype::LONGLONG), + rd, + rs1: rd, + imm: asm::Immediate::Value(56), + }); + } + 16 => { + context.insts.push(asm::Instruction::IType { + instr: asm::IType::slli(ir::Dtype::LONGLONG), + rd, + rs1: rd, + imm: asm::Immediate::Value(48), + }); + context.insts.push(asm::Instruction::IType { + instr: asm::IType::srai(ir::Dtype::LONGLONG), + rd, + rs1: rd, + imm: asm::Immediate::Value(48), + }); + } + 32 => context.insts.push(asm::Instruction::Pseudo( + asm::Pseudo::SextW { rd, rs: rd }, + )), + _ => todo!(), + } + } + } else if is_signed == target_is_signed { + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { rd, rs: rs1 })) + } else if *target_is_signed { + let rs2 = get_rhs_register(&value_dtype); + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Li { + rd: rs2, + imm: match target_width { + 1 => 1, + 8 => 0xFF, + 16 => 0xFFFF, + 32 => 0xFFFFFFFF, + 64 => 0xFFFFFFFFFFFFFFFF, + _ => unreachable!(), + }, + })); + context.insts.push(asm::Instruction::RType { + instr: asm::RType::And, + rd, + rs1, + rs2: Some(rs2), + }); + } else { + let rs2 = get_rhs_register(&value_dtype); + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Li { + rd: rs2, + imm: match width { + 1 => 1, + 8 => 0xFF, + 16 => 0xFFFF, + 32 => 0xFFFFFFFF, + 64 => 0xFFFFFFFFFFFFFFFF, + _ => unreachable!(), + }, + })); + context.insts.push(asm::Instruction::RType { + instr: asm::RType::And, + rd, + rs1, + rs2: Some(rs2), + }); + } + if is_spilled { + self.translate_store_result( + &rid, + target_dtype.clone(), + rd, + context, + ); + } } - (ir::Dtype::Float { .. }, ir::Dtype::Float { .. }) => { - let rd = get_rhs_register(target_dtype); - context.insts.push(asm::Instruction::RType { - instr: asm::RType::FcvtFloatToFloat { - from: asm::DataSize::try_from(value.dtype()).unwrap(), - to: asm::DataSize::try_from(target_dtype.clone()).unwrap(), - }, - rd, - rs1, - rs2: None, - }); - self.translate_store_result(&rid, target_dtype.clone(), rd, context); + (ir::Dtype::Int { .. }, ir::Dtype::Pointer { .. }) => { + if is_spilled { + self.translate_store_result( + &rid, + target_dtype.clone(), + rs1, + context, + ); + } else { + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { + rd, + rs: rs1, + })); + } } (ir::Dtype::Int { .. }, ir::Dtype::Float { .. }) => { - let rd = get_rhs_register(target_dtype); context.insts.push(asm::Instruction::RType { instr: asm::RType::fcvt_int_to_float( value.dtype(), @@ -1489,10 +1930,35 @@ impl Asmgen { rs1, rs2: None, }); - self.translate_store_result(&rid, target_dtype.clone(), rd, context); + if is_spilled { + self.translate_store_result( + &rid, + target_dtype.clone(), + rd, + context, + ); + } + } + (ir::Dtype::Float { .. }, ir::Dtype::Float { .. }) => { + context.insts.push(asm::Instruction::RType { + instr: asm::RType::FcvtFloatToFloat { + from: asm::DataSize::try_from(value.dtype()).unwrap(), + to: asm::DataSize::try_from(target_dtype.clone()).unwrap(), + }, + rd, + rs1, + rs2: None, + }); + if is_spilled { + self.translate_store_result( + &rid, + target_dtype.clone(), + rd, + context, + ); + } } (ir::Dtype::Float { .. }, ir::Dtype::Int { .. }) => { - let rd = get_rhs_register(target_dtype); context.insts.push(asm::Instruction::RType { instr: asm::RType::fcvt_float_to_int( value.dtype(), @@ -1502,106 +1968,96 @@ impl Asmgen { rs1, rs2: None, }); - self.translate_store_result(&rid, target_dtype.clone(), rd, context); + if is_spilled { + self.translate_store_result( + &rid, + target_dtype.clone(), + rd, + context, + ); + } } - _ => todo!(), + _ => unreachable!(), } } - ir::Instruction::Nop => (), ir::Instruction::GetElementPtr { ptr, offset, .. } => { + let ptr_dtype = ptr.dtype(); let offset_dtype = offset.dtype(); - let rs2 = get_rhs_register(&offset_dtype); - self.translate_load_operand(offset, rs2, context); + let rs2 = self.translate_load_operand( + offset, + get_rhs_register(&offset_dtype), + context, + ); match ptr { - ir::Operand::Constant(ir::Constant::GlobalVariable { name, dtype }) => { + ir::Operand::Constant(ir::Constant::GlobalVariable { + name: ptr_name, + .. + }) => { + let rd = rd.unwrap_or(get_res_register(&ptr_dtype)); context .insts .push(asm::Instruction::Pseudo(asm::Pseudo::La { rd: asm::Register::T0, - symbol: asm::Label(name.clone()), + symbol: asm::Label(ptr_name.clone()), })); - let rd = get_lhs_register(&ptr.dtype()); context.insts.push(asm::Instruction::RType { - instr: asm::RType::add(ptr.dtype()), + instr: asm::RType::add(ptr_dtype.clone()), rd, rs1: asm::Register::T0, rs2: Some(rs2), }); - self.translate_store_result(&rid, ptr.dtype(), rd, context); + if is_spilled { + self.translate_store_result(&rid, ptr_dtype, rd, context); + } } - ir::Operand::Register { - rid: ptr_rid, - dtype: ptr_dtype, - } => match ptr_rid { - ir::RegisterId::Temp { .. } | ir::RegisterId::Arg { .. } => { - let rs1 = get_lhs_register(ptr_dtype); - let rs2 = get_rhs_register(&offset_dtype); - self.translate_load_operand(ptr, rs1, context); - self.translate_load_operand(offset, rs2, context); + ir::Operand::Register { rid: ptr_rid, .. } => match ptr_rid { + ir::RegisterId::Local { aid } => { + let rs2 = self.translate_load_operand( + offset, + get_rhs_register(&offset_dtype), + context, + ); + let rd = rd.unwrap_or(get_res_register(&ptr_dtype)); + self.translate_addi( + rd, + asm::Register::S0, + context.stack_offsets[ptr_rid], + &mut context.insts, + ); context.insts.push(asm::Instruction::RType { instr: asm::RType::add(ptr_dtype.clone()), - rd: rs1, + rd, + rs1: rd, + rs2: Some(rs2), + }); + if is_spilled { + self.translate_store_result(&rid, ptr_dtype, rd, context); + } + } + _ => { + let rs1 = self.translate_load_operand( + ptr, + get_lhs_register(&ptr_dtype), + context, + ); + let rs2 = self.translate_load_operand( + offset, + get_rhs_register(&offset_dtype), + context, + ); + let rd = rd.unwrap_or(get_res_register(&ptr_dtype)); + context.insts.push(asm::Instruction::RType { + instr: asm::RType::add(ptr_dtype.clone()), + rd, rs1, rs2: Some(rs2), }); - self.translate_store_result(&rid, ptr.dtype(), rs1, context); - } - ir::RegisterId::Local { aid } => { - let rd = get_lhs_register(&ptr.dtype()); - let rs2 = get_rhs_register(&offset_dtype); - self.translate_load_operand(offset, rs2, context); - - let stack_offset = context.stack_offsets[ptr_rid]; - let imm_signed = stack_offset as i64; - if (-2048..=2047).contains(&imm_signed) { - context.insts.push(asm::Instruction::IType { - instr: asm::IType::ADDI, - rd, - rs1: asm::Register::S0, - imm: asm::Immediate::Value(stack_offset), - }); - context.insts.push(asm::Instruction::RType { - instr: asm::RType::add(ptr_dtype.clone()), - rd, - rs1: rd, - rs2: Some(rs2), - }); - self.translate_store_result( - &rid, - ptr_dtype.clone(), - rd, - context, - ); - } else { - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::Li { - rd: asm::Register::T3, - imm: stack_offset, - })); - context.insts.push(asm::Instruction::RType { - instr: asm::RType::add(ir::Dtype::int(64)), - rd: asm::Register::T3, - rs1: asm::Register::S0, - rs2: Some(asm::Register::T3), - }); - context.insts.push(asm::Instruction::RType { - instr: asm::RType::add(ptr_dtype.clone()), - rd, - rs1: rd, - rs2: Some(rs2), - }); - self.translate_store_result( - &rid, - ptr_dtype.clone(), - rd, - context, - ); + if is_spilled { + self.translate_store_result(&rid, ptr_dtype, rd, context); } } - _ => todo!(), }, - _ => todo!(), + _ => unreachable!(), } } _ => todo!(), @@ -1609,43 +2065,24 @@ impl Asmgen { } match &block.exit { - ir::BlockExit::Jump { arg } => { - context - .insts - .append(&mut self.translate_phinode(name, arg, context, structs)); - } + ir::BlockExit::Jump { arg } => context + .insts + .append(&mut self.translate_phinode(name, arg, context, structs)), ir::BlockExit::ConditionalJump { condition, arg_then, arg_else, } => { - let arg_then_label = if arg_then.args.is_empty() { - asm::Label::new(name, arg_then.bid) - } else { - let phinode_insts = self.translate_phinode(name, arg_then, context, structs); - let label = asm::Label(format!("{name}_P{}", self.phinode_counter)); - self.phinode_counter += 1; - context.new_blocks.push((label.clone(), phinode_insts)); - label - }; - let arg_else_label = if arg_else.args.is_empty() { - asm::Label::new(name, arg_else.bid) - } else { - let phinode_insts = self.translate_phinode(name, arg_else, context, structs); - let label = asm::Label(format!("{name}_P{}", self.phinode_counter)); - self.phinode_counter += 1; - context.new_blocks.push((label.clone(), phinode_insts)); - label - }; - let rs1 = get_lhs_register(&condition.dtype()); - self.translate_load_operand(condition, rs1, context); + let rs1_real = self.translate_load_operand(condition, rs1, context); + let arg_then_label = self.translate_label(name, arg_then, context, structs); + let arg_else_label = self.translate_label(name, arg_else, context, structs); context .insts .push(asm::Instruction::Pseudo(asm::Pseudo::Seqz { rd: rs1, - rs: rs1, + rs: rs1_real, })); context.insts.push(asm::Instruction::BType { instr: asm::BType::Bne, @@ -1655,40 +2092,25 @@ impl Asmgen { }); context.insts.push(asm::Instruction::Pseudo(asm::Pseudo::J { offset: arg_then_label, - })); // TODO: 최적화 가능? + })); // TODO: 최적화 가능? (점프 삭제) } ir::BlockExit::Switch { value, default, cases, } => { - let default_label = if default.args.is_empty() { - asm::Label::new(name, default.bid) - } else { - let phinode_insts = self.translate_phinode(name, default, context, structs); - let label = asm::Label(format!("{name}_P{}", self.phinode_counter)); - self.phinode_counter += 1; - context.new_blocks.push((label.clone(), phinode_insts)); - label - }; - - let rs1 = get_lhs_register(&value.dtype()); + let rs1 = + self.translate_load_operand(value, get_lhs_register(&value.dtype()), context); let rs2 = get_rhs_register(&value.dtype()); - self.translate_load_operand(value, rs1, context); + let default_label = self.translate_label(name, default, context, structs); for (case, arg) in cases { - self.translate_load_operand(&ir::Operand::constant(case.clone()), rs2, context); - - let case_label = if arg.args.is_empty() { - asm::Label::new(name, arg.bid) - } else { - let phinode_insts = self.translate_phinode(name, arg, context, structs); - let label = asm::Label(format!("{name}_P{}", self.phinode_counter)); - self.phinode_counter += 1; - context.new_blocks.push((label.clone(), phinode_insts)); - label - }; - + let _ = self.translate_load_operand( + &ir::Operand::constant(case.clone()), + rs2, + context, + ); + let case_label = self.translate_label(name, arg, context, structs); context.insts.push(asm::Instruction::BType { instr: asm::BType::Beq, rs1, @@ -1699,15 +2121,31 @@ impl Asmgen { context.insts.push(asm::Instruction::Pseudo(asm::Pseudo::J { offset: default_label, - })); // TODO: 최적화 가능? + })); // TODO: 최적화 가능? (점프 삭제) } ir::BlockExit::Return { value } => { - // TODO let dtype = value.dtype(); if is_integer(&dtype) { - self.translate_load_operand(value, asm::Register::A0, context); + let rd = self.translate_load_operand(value, asm::Register::A0, context); + if rd != asm::Register::A0 { + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { + rd: asm::Register::A0, + rs: rd, + })); + } } else if is_float(&dtype) { - self.translate_load_operand(value, asm::Register::FA0, context); + let rd = self.translate_load_operand(value, asm::Register::FA0, context); + if rd != asm::Register::FA0 { + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Fmv { + data_size: asm::DataSize::try_from(dtype).unwrap(), + rd: asm::Register::FA0, + rs: rd, + })); + } } else if let Some(size) = is_struct(&dtype, structs) { if size > 16 { context.insts.push(asm::Instruction::IType { @@ -1718,21 +2156,19 @@ impl Asmgen { }); match value { ir::Operand::Constant(constant) => match constant { - ir::Constant::Undef { .. } => (), // DO nothing + ir::Constant::Undef { .. } => (), // Do nothing _ => todo!(), }, ir::Operand::Register { rid, .. } => match rid { - ir::RegisterId::Temp { bid, iid } => { - self.translate_addi( - asm::Register::A1, - asm::Register::S0, - context.stack_offsets[rid], - &mut context.insts, - ); - } + ir::RegisterId::Temp { bid, iid } => self.translate_addi( + asm::Register::A1, + asm::Register::S0, + context.stack_offsets[rid], + &mut context.insts, + ), _ => todo!(), }, - _ => todo!(), + _ => unreachable!(), } context .insts @@ -1748,27 +2184,22 @@ impl Asmgen { } else { match value { ir::Operand::Constant(constant) => match constant { - ir::Constant::Undef { .. } => (), // DO nothing + ir::Constant::Undef { .. } => (), // Do nothing _ => todo!(), }, ir::Operand::Register { rid, .. } => match rid { - ir::RegisterId::Temp { bid, iid } => { - self.translate_addi( - asm::Register::T5, - asm::Register::S0, - context.stack_offsets[rid], - &mut context.insts, - ); - } + ir::RegisterId::Temp { bid, iid } => self.translate_addi( + asm::Register::T5, + asm::Register::S0, + context.stack_offsets[rid], + &mut context.insts, + ), _ => todo!(), }, - _ => todo!(), + _ => unreachable!(), } - let struct_name = - dtype.get_struct_name().unwrap().as_ref().unwrap().clone(); - let struct_dtype = structs[&struct_name].clone().unwrap(); - let fields = struct_dtype.get_struct_fields().unwrap().as_ref().unwrap(); + let (struct_dtype, fields) = get_struct_dtype(&dtype, structs); let mut num_int_fields = 0; let mut num_float_fields = 0; @@ -1776,7 +2207,7 @@ impl Asmgen { let (offset, _) = struct_dtype .get_offset_struct_field(field_dtype.name().unwrap(), structs) .unwrap(); - if is_integer(&field_dtype) { + if is_integer(field_dtype) { context.insts.push(asm::Instruction::IType { instr: asm::IType::load(field_dtype.deref().clone()), rd: asm::Register::arg( @@ -1787,7 +2218,7 @@ impl Asmgen { imm: asm::Immediate::Value(offset as u64), }); num_int_fields += 1; - } else if is_float(&field_dtype) { + } else if is_float(field_dtype) { context.insts.push(asm::Instruction::IType { instr: asm::IType::load(field_dtype.deref().clone()), rd: asm::Register::arg( @@ -1806,7 +2237,25 @@ impl Asmgen { } self.translate_epilogue(context); } - _ => todo!(), + _ => unreachable!(), + } + } + + fn translate_label( + &mut self, + name: &String, + arg: &ir::JumpArg, + context: &mut Context, + structs: &HashMap>, + ) -> asm::Label { + if arg.args.is_empty() { + asm::Label::new(name, arg.bid) + } else { + let phinode_insts = self.translate_phinode(name, arg, context, structs); + let label = asm::Label(format!("{name}_P{}", self.phinode_counter)); + self.phinode_counter += 1; + context.new_blocks.push((label.clone(), phinode_insts)); + label } } @@ -1815,74 +2264,65 @@ impl Asmgen { operand: &ir::Operand, rd: asm::Register, context: &mut Context, - ) { + ) -> asm::Register { match operand { - ir::Operand::Constant(c) => match c { - ir::Constant::Undef { dtype } => { - // Do nothing - } - ir::Constant::Int { - value, - width, - is_signed, - } => context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::Li { - rd, - imm: *value as u64, - })), - ir::Constant::Float { value, width } => { - if *width == 32 { + ir::Operand::Constant(constant) => { + match constant { + ir::Constant::Undef { .. } => (), // Do nothing + &ir::Constant::Int { value, .. } => { context .insts .push(asm::Instruction::Pseudo(asm::Pseudo::Li { - rd: asm::Register::T4, - imm: (**value as f32).to_bits() as u64, + rd, + imm: value as u64, })); - context.insts.push(asm::Instruction::RType { - instr: asm::RType::fmv_int_to_float(ir::Dtype::float(32)), - rd, - rs1: asm::Register::T4, - rs2: None, - }); - } else if *width == 64 { - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::Li { - rd: asm::Register::T4, - imm: value.to_bits(), - })); - context.insts.push(asm::Instruction::RType { - instr: asm::RType::fmv_int_to_float(ir::Dtype::float(64)), - rd, - rs1: asm::Register::T4, - rs2: None, - }); - } else { - unreachable!() } - } - ir::Constant::GlobalVariable { name, dtype } => { - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::La { + &ir::Constant::Float { value, width } => { + match width { + 32 => context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Li { + rd: asm::Register::T4, + imm: (*value as f32).to_bits() as u64, + })), + 64 => context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Li { + rd: asm::Register::T4, + imm: value.to_bits(), + })), + _ => unreachable!(), + } + context.insts.push(asm::Instruction::RType { + instr: asm::RType::fmv_int_to_float(ir::Dtype::float(width)), rd, - symbol: asm::Label(name.clone()), - })); + rs1: asm::Register::T4, + rs2: None, + }); + } + ir::Constant::GlobalVariable { name, .. } => { + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::La { + rd, + symbol: asm::Label(name.clone()), + })); + } + _ => unreachable!(), } - _ => { - println!("{c}"); - todo!() - } - }, + rd + } ir::Operand::Register { rid, dtype } => { - if let ir::RegisterId::Local { aid } = rid { + if matches!(rid, ir::RegisterId::Local { .. }) { self.translate_addi( rd, asm::Register::S0, context.stack_offsets[rid], &mut context.insts, ); + rd + } else if let Some(asm_register) = context.inference_graph.get_register(rid) { + asm_register } else { self.translate_load( asm::IType::load(dtype.clone()), @@ -1891,6 +2331,7 @@ impl Asmgen { context.stack_offsets[rid], &mut context.insts, ); + rd } } } @@ -1938,18 +2379,26 @@ impl Asmgen { ); } + // Swap Issue를 해결하기 위해 스택에 기존 phinode값 일괄 저장 let bid = arg.bid; for (aid, arg) in arg.args.iter().enumerate() { - let rs1 = get_lhs_register(&arg.dtype()); - self.translate_load( - asm::IType::load(arg.dtype()), - rs1, - asm::Register::S0, - context.stack_offsets[&ir::RegisterId::arg(bid, aid)], - &mut phinode_insts, - ); + let rid = ir::RegisterId::arg(bid, aid); + let arg_dtype = arg.dtype(); + let rs1 = if let Some(rs1) = context.inference_graph.get_register(&rid) { + rs1 + } else { + let rs1 = get_lhs_register(&arg_dtype); + self.translate_load( + asm::IType::load(arg_dtype.clone()), + rs1, + asm::Register::S0, + context.stack_offsets[&rid], + &mut phinode_insts, + ); + rs1 + }; self.translate_store( - asm::SType::store(arg.dtype()), + asm::SType::store(arg_dtype), asm::Register::Sp, rs1, phinode_stack_offsets[&aid], @@ -1958,61 +2407,97 @@ impl Asmgen { } for (aid, arg) in arg.args.iter().enumerate() { - let rs1 = get_lhs_register(&arg.dtype()); + let arg_dtype = arg.dtype(); + let rid = ir::RegisterId::arg(bid, aid); + let rd = context.inference_graph.get_register(&rid); + let is_spilled = rd.is_none(); + let rd = rd.unwrap_or(get_lhs_register(&arg_dtype)); + match arg { - ir::Operand::Constant(c) => match c { + ir::Operand::Constant(constant) => match constant { + ir::Constant::Undef { .. } => continue, // Do nothing ir::Constant::Int { value, width, is_signed, } => phinode_insts.push(asm::Instruction::Pseudo(asm::Pseudo::Li { - rd: rs1, + rd, imm: *value as u64, })), - ir::Constant::Undef { .. } => (), // DO nothing _ => todo!(), }, - ir::Operand::Register { rid, .. } => { + ir::Operand::Register { rid: arg_rid, .. } => { if let ir::RegisterId::Arg { bid: arg_bid, aid: arg_aid, - } = rid + } = arg_rid { if *arg_bid == bid { self.translate_load( - asm::IType::load(arg.dtype()), - rs1, + asm::IType::load(arg_dtype), + rd, asm::Register::Sp, phinode_stack_offsets[arg_aid], &mut phinode_insts, ); + } else if let Some(rs1) = context.inference_graph.get_register(arg_rid) { + if is_integer(&arg_dtype) { + phinode_insts.push(asm::Instruction::Pseudo(asm::Pseudo::Mv { + rd, + rs: rs1, + })); + } else if is_float(&arg_dtype) { + phinode_insts.push(asm::Instruction::Pseudo(asm::Pseudo::Fmv { + data_size: asm::DataSize::try_from(arg_dtype).unwrap(), + rd, + rs: rs1, + })); + } else { + todo!() + } } else { self.translate_load( - asm::IType::load(arg.dtype()), - rs1, + asm::IType::load(arg_dtype), + rd, asm::Register::S0, - context.stack_offsets[rid], + context.stack_offsets[arg_rid], &mut phinode_insts, ); } + } else if let Some(rs1) = context.inference_graph.get_register(arg_rid) { + if is_integer(&arg_dtype) { + phinode_insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { rd, rs: rs1 })); + } else if is_float(&arg_dtype) { + phinode_insts.push(asm::Instruction::Pseudo(asm::Pseudo::Fmv { + data_size: asm::DataSize::try_from(arg_dtype).unwrap(), + rd, + rs: rs1, + })); + } else { + todo!() + } } else { self.translate_load( - asm::IType::load(arg.dtype()), - rs1, + asm::IType::load(arg_dtype), + rd, asm::Register::S0, - context.stack_offsets[rid], + context.stack_offsets[arg_rid], &mut phinode_insts, ); } } } - self.translate_store( - asm::SType::store(arg.dtype()), - asm::Register::S0, - rs1, - context.stack_offsets[&ir::RegisterId::arg(bid, aid)], - &mut phinode_insts, - ); + + if is_spilled { + self.translate_store( + asm::SType::store(arg.dtype()), + asm::Register::S0, + rd, + context.stack_offsets[&ir::RegisterId::arg(bid, aid)], + &mut phinode_insts, + ); + } } if phinode_stack_allocations > 0 { @@ -2299,3 +2784,13 @@ fn is_struct(dtype: &ir::Dtype, structs: &HashMap>) -> None } } + +fn get_struct_dtype<'a>( + dtype: &'a ir::Dtype, + structs: &'a HashMap>, +) -> (&'a ir::Dtype, &'a Vec>) { + let struct_name = dtype.get_struct_name().unwrap().as_ref().unwrap(); + let struct_dtype = structs[struct_name].as_ref().unwrap(); + let fields = struct_dtype.get_struct_fields().unwrap().as_ref().unwrap(); + (struct_dtype, fields) +} diff --git a/test-asmgen.sh b/test-asmgen.sh new file mode 100755 index 0000000..cc23594 --- /dev/null +++ b/test-asmgen.sh @@ -0,0 +1,23 @@ +#!/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 -static "$OUTPUT_S_FILE" -o "$OUTPUT_BIN" + +# 3단계: QEMU로 RISC-V 바이너리 실행 +qemu-riscv64-static "$OUTPUT_BIN" +echo $?