#include "ir/PassManager.h" #include #include #include #include #include #include #include #include namespace ir { // Predeclaration of rebuild CFG helper void RebuildCFG(Function* func); bool RunMem2Reg(Function* func, Context& ctx) { // 1. Build dominator tree DominatorTree dom_tree(func); dom_tree.Run(); // 2. Identify promotable allocas std::vector promotable_allocas; for (const auto& bbPtr : func->GetBlocks()) { for (const auto& instPtr : bbPtr->GetInstructions()) { if (instPtr->GetOpcode() == Opcode::Alloca) { auto* alloca = static_cast(instPtr.get()); // Alloca of scalar type: i32 or float (pointers to i32/float in minimum IR) if (alloca->GetType()->IsPtrInt32() || alloca->GetType()->IsPtrFloat()) { // Verify all uses are load/store bool promotable = true; for (const auto& use : alloca->GetUses()) { auto* user = use.GetUser(); auto* inst_user = dynamic_cast(user); if (!inst_user) { promotable = false; break; } if (inst_user->GetOpcode() != Opcode::Load && inst_user->GetOpcode() != Opcode::Store) { promotable = false; break; } // For Store, alloca must be the pointer operand (operand index 1), not the value operand if (inst_user->GetOpcode() == Opcode::Store) { auto* store = static_cast(inst_user); if (store->GetPtr() != alloca) { promotable = false; break; } } } if (promotable) { promotable_allocas.push_back(alloca); } } } } } if (promotable_allocas.empty()) { return false; } // 3. For each alloca, find definition blocks and place Phi nodes // Maps each basic block and alloca to the inserted Phi instruction std::unordered_map> phi_nodes; std::unordered_set instructions_to_erase; for (auto* alloca : promotable_allocas) { std::vector def_blocks; for (const auto& use : alloca->GetUses()) { auto* inst = dynamic_cast(use.GetUser()); if (inst && inst->GetOpcode() == Opcode::Store) { def_blocks.push_back(inst->GetParent()); } } // DF-based Phi placement std::queue worklist; std::unordered_set added; std::unordered_set def_set(def_blocks.begin(), def_blocks.end()); for (auto* bb : def_blocks) { worklist.push(bb); added.insert(bb); } while (!worklist.empty()) { auto* x = worklist.front(); worklist.pop(); for (auto* y : dom_tree.GetDominanceFrontier(x)) { if (added.find(y) == added.end()) { // Place Phi node in Y std::shared_ptr ty = alloca->GetType()->IsPtrFloat() ? Type::GetFloatType() : Type::GetInt32Type(); auto phi = std::make_unique(ty, ctx.NextTemp()); auto* phi_ptr = phi.get(); // Insert Phi at the start of block Y y->InsertInstructionAtBegin(std::move(phi)); phi_nodes[y][alloca] = phi_ptr; added.insert(y); if (def_set.find(y) == def_set.end()) { worklist.push(y); } } } } } // 4. Rename variables using DFS traversal of dominator tree std::unordered_map> current_def; // Helper for generating default value auto get_default_value = [&](AllocaInst* alloca) -> Value* { if (alloca->GetType()->IsPtrFloat()) { return ctx.GetConstFloat(0.0f); } else { return ctx.GetConstInt(0); } }; // Traversal stack for DFS: stores (block, parent_block) struct TraversalNode { BasicBlock* bb; size_t child_idx; }; std::stack visit_stack; std::unordered_map>> pushed_defs; // DFS function std::function rename_dfs = [&](BasicBlock* bb) { auto& pushes = pushed_defs[bb]; // Push Phis in this block to current_def auto phi_it = phi_nodes.find(bb); if (phi_it != phi_nodes.end()) { for (const auto& pair : phi_it->second) { auto* alloca = pair.first; auto* phi = pair.second; current_def[alloca].push_back(phi); pushes.push_back({alloca, 1}); } } // Process loads and stores for (const auto& instPtr : bb->GetInstructions()) { auto* inst = instPtr.get(); if (inst->GetOpcode() == Opcode::Load) { auto* load = static_cast(inst); auto* ptr = load->GetPtr(); if (auto* alloca = dynamic_cast(ptr)) { if (std::find(promotable_allocas.begin(), promotable_allocas.end(), alloca) != promotable_allocas.end()) { auto& defs = current_def[alloca]; Value* val = defs.empty() ? get_default_value(alloca) : defs.back(); load->ReplaceAllUsesWith(val); instructions_to_erase.insert(load); } } } else if (inst->GetOpcode() == Opcode::Store) { auto* store = static_cast(inst); auto* ptr = store->GetPtr(); if (auto* alloca = dynamic_cast(ptr)) { if (std::find(promotable_allocas.begin(), promotable_allocas.end(), alloca) != promotable_allocas.end()) { current_def[alloca].push_back(store->GetValue()); pushes.push_back({alloca, 1}); instructions_to_erase.insert(store); } } } } // Fill Phi incoming values for CFG successors for (auto* succ : bb->GetSuccessors()) { auto succ_phi_it = phi_nodes.find(succ); if (succ_phi_it != phi_nodes.end()) { for (const auto& pair : succ_phi_it->second) { auto* alloca = pair.first; auto* phi = pair.second; auto& defs = current_def[alloca]; Value* val = defs.empty() ? get_default_value(alloca) : defs.back(); phi->AddIncoming(val, bb); } } } // Recurse to dominator tree children for (auto* child : dom_tree.GetDominatedBlocks(bb)) { rename_dfs(child); } // Pop definitions pushed in this block for (const auto& push : pushes) { auto* alloca = push.first; for (size_t k = 0; k < push.second; ++k) { if (!current_def[alloca].empty()) { current_def[alloca].pop_back(); } } } }; if (!func->GetBlocks().empty()) { rename_dfs(func->GetEntry()); } // 5. Clean up loads, stores and allocas for (auto* alloca : promotable_allocas) { instructions_to_erase.insert(alloca); } for (const auto& bbPtr : func->GetBlocks()) { std::vector to_remove; for (const auto& instPtr : bbPtr->GetInstructions()) { if (instructions_to_erase.find(instPtr.get()) != instructions_to_erase.end()) { to_remove.push_back(instPtr.get()); } } for (auto* inst : to_remove) { bbPtr->EraseInstruction(inst); } } return true; } } // namespace ir