From 58102acee33f0f2d5bb3648c35cb8dd9c77d74c7 Mon Sep 17 00:00:00 2001 From: static Date: Tue, 17 Jun 2025 22:51:58 +0000 Subject: [PATCH] HW8 (7) --- src/asmgen/mod.rs | 472 +++++++++++++++++++++++++++++++++------------- 1 file changed, 340 insertions(+), 132 deletions(-) diff --git a/src/asmgen/mod.rs b/src/asmgen/mod.rs index ad2b686..de4b626 100644 --- a/src/asmgen/mod.rs +++ b/src/asmgen/mod.rs @@ -40,8 +40,8 @@ impl Translate for Asmgen { struct InferenceGraph { edges: HashSet<(ir::RegisterId, ir::RegisterId)>, vertices: HashMap, - has_call: bool, - is_a0_return_pointer: bool, + analysis: Analysis, + lives: HashMap>, } impl InferenceGraph { @@ -192,6 +192,16 @@ impl InferenceGraph { .or_insert_with(HashSet::new), uses, ) || changed; + + let phinodes = (0..block.phinodes.len()) + .map(|i| ir::RegisterId::arg(*bid, i)) + .collect(); + changed = extend_set( + lives + .entry(ir::RegisterId::temp(*bid, 0)) + .or_insert_with(HashSet::new), + phinodes, + ) || changed; } if !changed { @@ -212,95 +222,57 @@ impl InferenceGraph { } } - 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)); - } - } - } + // 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)); + // } + // } + // } let mut num_of_edges = HashMap::new(); for (rid1, _) in &edges { *num_of_edges.entry(*rid1).or_insert(0) += 1; } - let (mut num_int_args, mut num_float_args, primitive_arg_reg_index) = - get_number_of_register_arguments(&signature.ret, &signature.params, structs); - let mut has_call = false; - let mut is_a0_return_pointer = false; + let analysis = analyze_function(code, signature, 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 - has_call = true; - 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; - has_call = true; // memcpy - 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; - has_call = true; // memcpy - break 'outer; + if !analysis.has_memcpy_in_prologue { + for (aid, dtype) in code.blocks[&code.bid_init].phinodes.iter().enumerate() { + let rid = ir::RegisterId::arg(code.bid_init, aid); + if analysis.is_temporary2(&rid, &lives) { + if is_integer(dtype) { + let (_, asm_reg) = vertices.get_mut(&rid).unwrap(); + *asm_reg = asm::Register::arg( + asm::RegisterType::Integer, + analysis.primitive_arg_reg_index[&aid] as usize, + ); + } else if is_float(dtype) { + let (_, asm_reg) = vertices.get_mut(&rid).unwrap(); + *asm_reg = asm::Register::arg( + asm::RegisterType::FloatingPoint, + analysis.primitive_arg_reg_index[&aid] as usize, + ); } } } } - if let Some(size) = is_struct(&signature.ret, structs) { - if size > 16 { - num_int_args = 8; - num_float_args = 8; - has_call = true; // memcpy - is_a0_return_pointer = true; - } - } - - if !has_call { - for (aid, dtype) in code.blocks[&code.bid_init].phinodes.iter().enumerate() { - let rid = ir::RegisterId::arg(code.bid_init, aid); - if is_integer(dtype) { + for (bid, iid, _, reg) in &analysis.calls { + let rid = ir::RegisterId::temp(*bid, *iid); + if analysis.is_temporary2(&rid, &lives) { + if let Some(reg) = reg { let (_, asm_reg) = vertices.get_mut(&rid).unwrap(); - *asm_reg = asm::Register::arg( - asm::RegisterType::Integer, - primitive_arg_reg_index[&aid] as usize, - ); - } else if is_float(dtype) { - let (_, asm_reg) = vertices.get_mut(&rid).unwrap(); - *asm_reg = asm::Register::arg( - asm::RegisterType::FloatingPoint, - primitive_arg_reg_index[&aid] as usize, - ); + *asm_reg = *reg; } } } @@ -363,7 +335,7 @@ impl InferenceGraph { } }) .collect(), - num_int_args as usize, + 0, ); let smallest_saved_reg = smallest_missing_integer( &neighbor_registers @@ -378,7 +350,7 @@ impl InferenceGraph { .collect(), 1, ); // s0는 못 씀 - if smallest_arg_reg <= 7 { + if smallest_arg_reg <= 7 && analysis.is_temporary2(&rid, &lives) { let _unused = vertices.insert( rid, ( @@ -409,7 +381,7 @@ impl InferenceGraph { } }) .collect(), - num_float_args as usize, + 0, ); let smallest_saved_reg = smallest_missing_integer( &neighbor_registers @@ -424,7 +396,7 @@ impl InferenceGraph { .collect(), 0, ); - if smallest_arg_reg <= 7 { + if smallest_arg_reg <= 7 && analysis.is_temporary2(&rid, &lives) { let _unused = vertices.insert( rid, ( @@ -454,8 +426,8 @@ impl InferenceGraph { InferenceGraph { edges, vertices, - has_call, - is_a0_return_pointer, + analysis, + lives, } } @@ -501,6 +473,117 @@ fn smallest_missing_integer(set: &HashSet, start: usize) -> usize { i } +struct Analysis { + num_int_args: i32, + num_float_args: i32, + primitive_arg_reg_index: HashMap, + is_a0_return_pointer: bool, + has_memcpy_in_prologue: bool, + calls: Vec<( + ir::BlockId, + usize, + HashSet, + Option, + )>, + has_call: bool, +} + +impl Analysis { + fn is_temporary2( + &self, + reg: &ir::RegisterId, + lives: &HashMap>, + ) -> bool { + for (call_bid, call_iid, call_args, _) in &self.calls { + let lives_before = lives + .get(&ir::RegisterId::temp(*call_bid, *call_iid)) + .unwrap(); + let lives_after = lives + .get(&ir::RegisterId::temp(*call_bid, call_iid + 1)) + .unwrap(); + if lives_before.contains(reg) && (call_args.contains(reg) || lives_after.contains(reg)) + { + return false; + } + } + + true + } +} + +fn analyze_function( + code: &ir::FunctionDefinition, + signature: &ir::FunctionSignature, + structs: &HashMap>, +) -> Analysis { + let (num_int_args, num_float_args, primitive_arg_reg_index) = + get_number_of_register_arguments(&signature.ret, &signature.params, structs); + let is_a0_return_pointer = is_struct(&signature.ret, structs).is_some_and(|size| size > 16); + let mut calls = Vec::new(); + + for (bid, block) in &code.blocks { + for (iid, inst) in block.instructions.iter().enumerate() { + match inst.deref() { + ir::Instruction::Call { callee, args, .. } => { + let mut used = HashSet::new(); + mark_as_used(callee, &mut used); + for arg in args { + mark_as_used(arg, &mut used); + } + + let return_type = callee + .dtype() + .get_pointer_inner() + .unwrap() + .get_function_inner() + .unwrap() + .0 + .clone(); + let ret_asm_reg = if is_integer(&return_type) { + Some(asm::Register::A0) + } else if is_float(&return_type) { + Some(asm::Register::FA0) + } else { + None + }; + + calls.push((*bid, iid, used, ret_asm_reg)); + } + ir::Instruction::Store { value, .. } => { + if is_struct(&value.dtype(), structs).is_some() { + calls.push((*bid, iid, HashSet::new(), None)); // memcpy + } + } + ir::Instruction::Load { ptr } => { + if is_struct(ptr.dtype().get_pointer_inner().unwrap(), structs).is_some() { + calls.push((*bid, iid, HashSet::new(), None)); // memcpy + } + } + _ => (), + } + } + } + + let has_memcpy_in_prologue = signature.params.iter().any(|param| { + if let Some(size) = is_struct(param, structs) { + size > 16 + } else { + false + } + }); + let has_call = has_memcpy_in_prologue || !calls.is_empty(); + + Analysis { + num_int_args, + num_float_args, + primitive_arg_reg_index, + is_a0_return_pointer, + has_memcpy_in_prologue, + calls, + has_call, + } +} + struct Context { insts: Vec, stack_offsets: HashMap, @@ -649,11 +732,11 @@ impl Asmgen { stack_allocation += 8; // s0 let mut saved_reg_offset = 8; - if inference_graph.has_call { + if inference_graph.analysis.has_call { stack_allocation += 8; // ra saved_reg_offset += 8; } - if inference_graph.is_a0_return_pointer { + if inference_graph.analysis.is_a0_return_pointer { stack_allocation += 8; // a0 saved_reg_offset += 8; } @@ -718,7 +801,7 @@ impl Asmgen { stack_allocation, &mut insts, ); - if inference_graph.has_call { + if inference_graph.analysis.has_call { insts.push(asm::Instruction::SType { instr: asm::SType::SD, rs1: asm::Register::S0, @@ -726,7 +809,7 @@ impl Asmgen { imm: asm::Immediate::Value(!16 + 1), }); } - if inference_graph.is_a0_return_pointer { + if inference_graph.analysis.is_a0_return_pointer { insts.push(asm::Instruction::SType { instr: asm::SType::SD, rs1: asm::Register::S0, @@ -1053,7 +1136,7 @@ impl Asmgen { }); } - if context.inference_graph.has_call { + if context.inference_graph.analysis.has_call { context.insts.push(asm::Instruction::IType { instr: asm::IType::LD, rd: asm::Register::Ra, @@ -1960,12 +2043,14 @@ impl Asmgen { // TODO if is_integer(return_type) { if let Some(rd) = context.inference_graph.get_register(&rid) { - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { - rd, - rs: asm::Register::A0, - })); + if rd != asm::Register::A0 { + context + .insts + .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { + rd, + rs: asm::Register::A0, + })); + } } else { self.translate_store_result( &rid, @@ -1976,14 +2061,16 @@ impl Asmgen { } } else if is_float(return_type) { 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, - })); + if rd != asm::Register::FA0 { + 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, @@ -2153,9 +2240,14 @@ impl Asmgen { } } } else if is_signed == target_is_signed { - context - .insts - .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { rd, rs: rs1 })) + if rd != rs1 { + 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 @@ -2216,7 +2308,7 @@ impl Asmgen { rs1, context, ); - } else { + } else if rd != rs1 { context .insts .push(asm::Instruction::Pseudo(asm::Pseudo::Mv { @@ -2447,7 +2539,7 @@ impl Asmgen { let default_label = self.translate_label(name, default, context, structs); for (case, arg) in cases { - let _ = self.translate_load_operand( + let rs2 = self.translate_load_operand( &ir::Operand::constant(case.clone()), rs2, context, @@ -2611,6 +2703,7 @@ impl Asmgen { ir::Operand::Constant(constant) => { match constant { ir::Constant::Undef { .. } => (), // Do nothing + ir::Constant::Int { value: 0, .. } => return asm::Register::Zero, &ir::Constant::Int { value, .. } => { context .insts @@ -2703,7 +2796,14 @@ impl Asmgen { structs: &HashMap>, ) -> Vec { let bid = arg.bid; + let mut lives = context + .inference_graph + .lives + .get(&ir::RegisterId::temp(bid, 0)) + .cloned() + .unwrap_or_default(); let mut referenced_org_phinodes = HashSet::new(); + for arg in &arg.args { if let ir::Operand::Register { rid: ir::RegisterId::Arg { bid: arg_bid, aid }, @@ -2714,13 +2814,67 @@ impl Asmgen { let _ = referenced_org_phinodes.insert(aid); } } + if let ir::Operand::Register { rid, .. } = arg { + let _ = lives.insert(*rid); + } } + let mut used_asm_registers = lives + .iter() + .filter_map(|rid| context.inference_graph.get_register(rid)) + .collect::>(); + + let mut phinode_temp_registers = HashMap::new(); let mut phinode_stack_offsets = HashMap::new(); let mut phinode_stack_allocations = 0; for (aid, arg) in arg.args.iter().enumerate() { if referenced_org_phinodes.contains(&aid) { + let dtype = arg.dtype(); + // 자리가 있으면 기존 phinode 값을 레지스터에 백업 + if is_integer(&dtype) { + let used_asm_a_registers = used_asm_registers + .iter() + .filter_map(|asm_reg| { + if let asm::Register::Arg(asm::RegisterType::Integer, i) = asm_reg { + Some(*i) + } else { + None + } + }) + .collect(); + let smallest_a = smallest_missing_integer(&used_asm_a_registers, 0); + if smallest_a <= 7 { + let asm_reg = asm::Register::arg(asm::RegisterType::Integer, smallest_a); + let _ = phinode_temp_registers.insert(aid, asm_reg); + let _ = used_asm_registers.insert(asm_reg); + continue; + } + } else if is_float(&dtype) { + let used_asm_a_registers = used_asm_registers + .iter() + .filter_map(|asm_reg| { + if let asm::Register::Arg(asm::RegisterType::FloatingPoint, i) = asm_reg + { + Some(*i) + } else { + None + } + }) + .collect(); + let smallest_a = smallest_missing_integer(&used_asm_a_registers, 0); + if smallest_a <= 7 { + let asm_reg = + asm::Register::arg(asm::RegisterType::FloatingPoint, smallest_a); + let _ = phinode_temp_registers.insert(aid, asm_reg); + let _ = used_asm_registers.insert(asm_reg); + continue; + } + } else { + todo!() + } + + // 자리가 없으면 스택에 let _ = phinode_stack_offsets.insert(aid, phinode_stack_allocations); phinode_stack_allocations += ceil_to_multiple_of_16(get_dtype_size(&arg.dtype(), structs)); @@ -2737,32 +2891,50 @@ impl Asmgen { ); } - // Swap Issue를 해결하기 위해 스택에 기존 phinode값 저장 (필요한 것만) + // Swap Issue를 해결하기 위해 스택/레지스터에 기존 phinode값 저장 (필요한 것만) let bid = arg.bid; for (aid, arg) in arg.args.iter().enumerate() { if referenced_org_phinodes.contains(&aid) { 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 + let rs1: asm::Register = + 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 + }; + if let Some(asm_reg) = phinode_temp_registers.get(&aid) { + if is_integer(&arg_dtype) { + phinode_insts.push(asm::Instruction::Pseudo(asm::Pseudo::Mv { + rd: *asm_reg, + 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: *asm_reg, + rs: rs1, + })); + } else { + unreachable!() + } } else { - let rs1 = get_lhs_register(&arg_dtype); - self.translate_load( - asm::IType::load(arg_dtype.clone()), + self.translate_store( + asm::SType::store(arg_dtype), + asm::Register::Sp, rs1, - asm::Register::S0, - context.stack_offsets[&rid], + phinode_stack_offsets[&aid], &mut phinode_insts, ); - rs1 - }; - self.translate_store( - asm::SType::store(arg_dtype), - asm::Register::Sp, - rs1, - phinode_stack_offsets[&aid], - &mut phinode_insts, - ); + } } } @@ -2776,6 +2948,23 @@ impl Asmgen { match arg { ir::Operand::Constant(constant) => match constant { ir::Constant::Undef { .. } => continue, // Do nothing + ir::Constant::Int { value: 0, .. } => { + if is_spilled { + self.translate_store( + asm::SType::store(arg.dtype()), + asm::Register::S0, + asm::Register::Zero, + context.stack_offsets[&ir::RegisterId::arg(bid, aid)], + &mut phinode_insts, + ); + continue; + } else { + phinode_insts.push(asm::Instruction::Pseudo(asm::Pseudo::Mv { + rd, + rs: asm::Register::Zero, + })); + } + } ir::Constant::Int { value, width, @@ -2793,13 +2982,32 @@ impl Asmgen { } = arg_rid { if *arg_bid == bid { - self.translate_load( - asm::IType::load(arg_dtype), - rd, - asm::Register::Sp, - phinode_stack_offsets[arg_aid], - &mut phinode_insts, - ); + if let Some(asm_reg) = phinode_temp_registers.get(arg_aid) { + if is_integer(&arg_dtype) { + phinode_insts.push(asm::Instruction::Pseudo(asm::Pseudo::Mv { + rd, + rs: *asm_reg, + })); + } 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: *asm_reg, + }, + )); + } else { + unreachable!() + } + } else { + self.translate_load( + 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 {