diff --git a/bench/run.sh b/bench/run.sh new file mode 100755 index 0000000..7ac6ed5 --- /dev/null +++ b/bench/run.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +(make clean && make run) | tail -n 1 + diff --git a/compile.sh b/compile.sh new file mode 100755 index 0000000..aec94de --- /dev/null +++ b/compile.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# 사용법 확인 +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + echo "Example: $0 examples/c/unary.c" + exit 1 +fi + +cargo run --features=build-bin -- "$1" --optimize --iroutput > "hello.ir" +cargo run --features=build-bin -- "$1" --optimize > "hello.S" diff --git a/src/asmgen/mod.rs b/src/asmgen/mod.rs index 0ec285d..d784b28 100644 --- a/src/asmgen/mod.rs +++ b/src/asmgen/mod.rs @@ -43,7 +43,11 @@ struct InferenceGraph { } impl InferenceGraph { - fn new(code: &ir::FunctionDefinition) -> Self { + fn new( + code: &ir::FunctionDefinition, + signature: &ir::FunctionSignature, + structs: &HashMap>, + ) -> Self { let cfg = make_cfg(code); let reverse_cfg = reverse_cfg(&cfg); let domtree = Domtree::new(code.bid_init, &cfg, &reverse_cfg); @@ -228,10 +232,56 @@ impl InferenceGraph { *num_of_edges.entry(*rid1).or_insert(0) += 1; } + let (mut num_int_args, mut num_float_args) = + get_number_of_register_arguments(&signature.ret, &signature.params, structs); + + 'outer: for block in code.blocks.values() { + for inst in &block.instructions { + if let ir::Instruction::Call { + callee, + args, + return_type, + } = inst.deref() + { + // let (new_num_int_args, new_num_float_args) = get_number_of_register_arguments( + // return_type, + // &args.iter().map(|operand| operand.dtype()).collect(), + // structs, + // ); + // num_int_args = std::cmp::max(num_int_args, new_num_int_args); + // num_float_args = std::cmp::max(num_float_args, new_num_float_args); + + num_int_args = 8; + num_float_args = 8; // call이 있는 경우 a는 사용 x + break 'outer; + } else if let ir::Instruction::Store { value, .. } = inst.deref() { + if is_struct(&value.dtype(), structs).is_some() { + num_int_args = 8; + num_float_args = 8; + break 'outer; + } + } else if let ir::Instruction::Load { ptr } = inst.deref() { + if is_struct(ptr.dtype().get_pointer_inner().unwrap(), structs).is_some() { + num_int_args = 8; + num_float_args = 8; + break 'outer; + } + } + } + } + + if let Some(size) = is_struct(&signature.ret, structs) { + if size > 16 { + num_int_args = 8; + num_float_args = 8; + } + } + 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; @@ -252,44 +302,121 @@ impl InferenceGraph { .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_integer(dtype) + && matches!( + reg, + asm::Register::Saved(asm::RegisterType::Integer, _) + | asm::Register::Arg(asm::RegisterType::Integer, _) + ) + { + return Some(*reg); } - if is_float(dtype) { - if let asm::Register::Saved(asm::RegisterType::FloatingPoint, i) = reg { - return Some(*i); - } + if is_float(dtype) + && matches!( + reg, + asm::Register::Saved(asm::RegisterType::FloatingPoint, _) + | asm::Register::Arg(asm::RegisterType::FloatingPoint, _) + ) + { + return Some(*reg); } 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 smallest_arg_reg = smallest_missing_integer( + &neighbor_registers + .iter() + .filter_map(|reg| { + if let asm::Register::Arg(_, i) = reg { + Some(*i) + } else { + None + } + }) + .collect(), + num_int_args as usize, + ); + let smallest_saved_reg = smallest_missing_integer( + &neighbor_registers + .iter() + .filter_map(|reg| { + if let asm::Register::Saved(_, i) = reg { + Some(*i) + } else { + None + } + }) + .collect(), + 1, + ); // s0는 못 씀 + if smallest_arg_reg <= 7 { let _unused = vertices.insert( rid, ( dtype.clone(), - asm::Register::Saved(asm::RegisterType::Integer, my_i), + asm::Register::arg(asm::RegisterType::Integer, smallest_arg_reg), ), ); + } else if smallest_saved_reg <= 11 { + let _unused = vertices.insert( + rid, + ( + dtype.clone(), + asm::Register::saved(asm::RegisterType::Integer, smallest_saved_reg), + ), + ); + } else { + // Spilling } } else if is_float(dtype) { - let my_i = smallest_missing_integer(&neighbor_registers, 0); // s0는 못 씀 - if my_i >= 12 { - // TODO: Spilling or 레지스터 쪼개기 필요 - } else { + let smallest_arg_reg = smallest_missing_integer( + &neighbor_registers + .iter() + .filter_map(|reg| { + if let asm::Register::Arg(_, i) = reg { + Some(*i) + } else { + None + } + }) + .collect(), + num_float_args as usize, + ); + let smallest_saved_reg = smallest_missing_integer( + &neighbor_registers + .iter() + .filter_map(|reg| { + if let asm::Register::Saved(_, i) = reg { + Some(*i) + } else { + None + } + }) + .collect(), + 0, + ); + if smallest_arg_reg <= 7 { let _unused = vertices.insert( rid, ( dtype.clone(), - asm::Register::Saved(asm::RegisterType::FloatingPoint, my_i), + asm::Register::arg(asm::RegisterType::FloatingPoint, smallest_arg_reg), ), ); + } else if smallest_saved_reg <= 11 { + let _unused = vertices.insert( + rid, + ( + dtype.clone(), + asm::Register::saved( + asm::RegisterType::FloatingPoint, + smallest_saved_reg, + ), + ), + ); + } else { + // Spilling } } else { // TODO: Spilling or 레지스터 쪼개기 필요 @@ -402,7 +529,7 @@ impl Asmgen { definition, } => { if let Some(definition) = definition { - let graph = InferenceGraph::new(definition); + let graph = InferenceGraph::new(definition, signature, structs); // println!("asdf: {:?}", graph.vertices); @@ -487,7 +614,7 @@ impl Asmgen { stack_allocation += 24; // s0, ra, a0 - let num_int_regs = inference_graph + let num_int_saved_regs = inference_graph .vertices .values() .filter_map(|(_, reg)| { @@ -499,7 +626,7 @@ impl Asmgen { }) .collect::>() .len(); - let num_float_regs = inference_graph + let num_float_saved_regs = inference_graph .vertices .values() .filter_map(|(_, reg)| { @@ -511,7 +638,7 @@ impl Asmgen { }) .collect::>() .len(); - stack_allocation += ((num_int_regs + num_float_regs) * 8) as u64; + stack_allocation += ((num_int_saved_regs + num_float_saved_regs) * 8) as u64; if stack_allocation % 16 != 0 { // 스택은 16바이트 정렬 @@ -556,7 +683,7 @@ impl Asmgen { }); // S1~레지스터 및 FS0~ 레지스터 백업 - for i in 0..num_int_regs { + for i in 0..num_int_saved_regs { insts.push(asm::Instruction::SType { instr: asm::SType::SD, rs1: asm::Register::S0, @@ -567,12 +694,12 @@ impl Asmgen { imm: asm::Immediate::Value((!(32 + i * 8) + 1) as u64), }); } - for i in 0..num_float_regs { + for i in 0..num_float_saved_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), + imm: asm::Immediate::Value((!(32 + num_int_saved_regs * 8 + i * 8) + 1) as u64), }); } @@ -729,80 +856,82 @@ impl Asmgen { } } - insts.push(asm::Instruction::IType { - instr: asm::IType::ADDI, - rd: asm::Register::Sp, - rs1: asm::Register::Sp, - imm: asm::Immediate::Value(!16 + 1), - }); - insts.push(asm::Instruction::SType { - instr: asm::SType::SD, - rs1: asm::Register::Sp, - rs2: asm::Register::A1, - imm: asm::Immediate::Value(0), - }); - insts.push(asm::Instruction::SType { - instr: asm::SType::SD, - rs1: asm::Register::Sp, - rs2: asm::Register::A2, - imm: asm::Immediate::Value(8), - }); + if !large_struct.is_empty() { + insts.push(asm::Instruction::IType { + instr: asm::IType::ADDI, + rd: asm::Register::Sp, + rs1: asm::Register::Sp, + imm: asm::Immediate::Value(!16 + 1), + }); + insts.push(asm::Instruction::SType { + instr: asm::SType::SD, + rs1: asm::Register::Sp, + rs2: asm::Register::A1, + imm: asm::Immediate::Value(0), + }); + insts.push(asm::Instruction::SType { + instr: asm::SType::SD, + rs1: asm::Register::Sp, + rs2: asm::Register::A2, + imm: asm::Immediate::Value(8), + }); - for (i, dtype) in signature.params.iter().enumerate() { - 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]; + for (i, dtype) in signature.params.iter().enumerate() { + 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]; - self.translate_addi( - asm::Register::A0, - asm::Register::S0, - stack_offset, - &mut insts, - ); + self.translate_addi( + asm::Register::A0, + asm::Register::S0, + stack_offset, + &mut insts, + ); - match reg_index { - 0 => insts.push(asm::Instruction::IType { - instr: asm::IType::LD, - rd: asm::Register::A1, - rs1: asm::Register::S0, - imm: asm::Immediate::Value(!24 + 1), - }), - 1 => insts.push(asm::Instruction::IType { - instr: asm::IType::LD, - rd: asm::Register::A1, - rs1: asm::Register::Sp, - imm: asm::Immediate::Value(0), - }), - 2 => insts.push(asm::Instruction::IType { - instr: asm::IType::LD, - rd: asm::Register::A1, - rs1: asm::Register::Sp, - imm: asm::Immediate::Value(8), - }), - _ => insts.push(asm::Instruction::Pseudo(asm::Pseudo::Mv { - rd: asm::Register::A1, - rs: asm::Register::arg(asm::RegisterType::Integer, reg_index), - })), + match reg_index { + 0 => insts.push(asm::Instruction::IType { + instr: asm::IType::LD, + rd: asm::Register::A1, + rs1: asm::Register::S0, + imm: asm::Immediate::Value(!24 + 1), + }), + 1 => insts.push(asm::Instruction::IType { + instr: asm::IType::LD, + rd: asm::Register::A1, + rs1: asm::Register::Sp, + imm: asm::Immediate::Value(0), + }), + 2 => insts.push(asm::Instruction::IType { + instr: asm::IType::LD, + rd: asm::Register::A1, + rs1: asm::Register::Sp, + imm: asm::Immediate::Value(8), + }), + _ => insts.push(asm::Instruction::Pseudo(asm::Pseudo::Mv { + rd: asm::Register::A1, + rs: asm::Register::arg(asm::RegisterType::Integer, reg_index), + })), + } + + insts.push(asm::Instruction::Pseudo(asm::Pseudo::Li { + rd: asm::Register::A2, + imm: size, + })); + insts.push(asm::Instruction::Pseudo(asm::Pseudo::Call { + offset: asm::Label(String::from("memcpy")), + })); } - - insts.push(asm::Instruction::Pseudo(asm::Pseudo::Li { - rd: asm::Register::A2, - imm: size, - })); - insts.push(asm::Instruction::Pseudo(asm::Pseudo::Call { - offset: asm::Label(String::from("memcpy")), - })); } } - } - insts.push(asm::Instruction::IType { - instr: asm::IType::ADDI, - rd: asm::Register::Sp, - rs1: asm::Register::Sp, - imm: asm::Immediate::Value(16), - }); + insts.push(asm::Instruction::IType { + instr: asm::IType::ADDI, + rd: asm::Register::Sp, + rs1: asm::Register::Sp, + imm: asm::Immediate::Value(16), + }); + } Context { insts, @@ -2012,11 +2141,6 @@ impl Asmgen { } 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, @@ -2040,11 +2164,6 @@ impl Asmgen { 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()), @@ -2794,3 +2913,76 @@ fn get_struct_dtype<'a>( let fields = struct_dtype.get_struct_fields().unwrap().as_ref().unwrap(); (struct_dtype, fields) } + +fn get_number_of_register_arguments( + return_type: &ir::Dtype, + params: &Vec, + structs: &HashMap>, +) -> (i32, i32) { + let mut num_int_args = 0; + let mut num_float_args = 0; + + if is_struct(return_type, structs).is_some_and(|size| size > 16) { + num_int_args += 1; + } + + for dtype in params { + if is_integer(dtype) { + num_int_args += 1; + } else if is_float(dtype) { + num_float_args += 1; + } else if let Some(size) = is_struct(dtype, structs) { + if size > 16 { + num_int_args += 1; + } else { + let (struct_dtype, fields) = get_struct_dtype(dtype, structs); + let mut is_packing = false; + let mut packing_start_offset = 0; + let mut packing_size = 0; + + for field_dtype in fields { + 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) { + 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 { + num_int_args += 1; + packing_start_offset = offset; + packing_size = field_size; + } + } else if is_float(field_dtype) { + if is_packing { + num_int_args += 1; + is_packing = false; + } + + num_float_args += 1; + } else { + todo!() + } + } + + if is_packing { + num_int_args += 1; + } + } + } + } + + if num_int_args == 0 && is_integer(return_type) { + num_int_args = 1; + } else if num_float_args == 0 && is_float(return_type) { + num_float_args = 1; + } + + (num_int_args, num_float_args) +}