From de92397fbf2f54ab394827bc93c71822454bb857 Mon Sep 17 00:00:00 2001 From: Mikhail Kaskov Date: Tue, 5 Mar 2024 13:52:53 +0300 Subject: [PATCH] Added linear scan regalloc + Added liveness analyzer + Improved linear order + Fixed GCM --- .github/workflows/c-cpp.yml | 4 + CMakeLists.txt | 3 +- src/graph.h | 6 +- src/inst.cpp | 5 + src/inst.h | 35 +- src/optimizations/analysis/linear_order.cpp | 76 +++- src/optimizations/analysis/linear_order.h | 11 +- .../analysis/liveness_analyzer.cpp | 154 ++++++-- .../analysis/liveness_analyzer.h | 174 ++++++++- src/optimizations/gcm.cpp | 2 +- src/optimizations/linear_scan.cpp | 78 ++++ src/optimizations/linear_scan.h | 89 +++++ src/pass_manager.h | 5 + tests/analysis_tests.cpp | 352 +++++++++++++++++- 14 files changed, 951 insertions(+), 43 deletions(-) create mode 100644 src/optimizations/linear_scan.cpp create mode 100644 src/optimizations/linear_scan.h create mode 100644 src/pass_manager.h diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index f26d116..5c017ab 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -10,6 +10,9 @@ jobs: build: runs-on: ubuntu-latest + env: + CC: gcc-9 + CXX: g++-9 steps: - uses: actions/checkout@v3 @@ -37,3 +40,4 @@ jobs: cd build valgrind tests/analysis_tests -s valgrind tests/graph_tests -s + diff --git a/CMakeLists.txt b/CMakeLists.txt index b8ddb2f..ffc5ea9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(Compiler) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_CXX_FLAGS "-pedantic -Wall -Wextra -Werror -Wshadow") +set(CMAKE_CXX_FLAGS "-pedantic -Wall -Wextra -Werror -Wshadow -g") set(ROOT_COMPILER ${CMAKE_SOURCE_DIR}) set(COMPILER_SOURCES @@ -19,6 +19,7 @@ set(COMPILER_SOURCES ${CMAKE_SOURCE_DIR}/src/optimizations/gcm.cpp ${CMAKE_SOURCE_DIR}/src/optimizations/analysis/linear_order.cpp ${CMAKE_SOURCE_DIR}/src/optimizations/analysis/liveness_analyzer.cpp + ${CMAKE_SOURCE_DIR}/src/optimizations/linear_scan.cpp ) add_subdirectory(tests) diff --git a/src/graph.h b/src/graph.h index e856808..76c15e6 100644 --- a/src/graph.h +++ b/src/graph.h @@ -4,6 +4,8 @@ namespace compiler { +class LiveInterval; + class Graph { public: @@ -130,13 +132,15 @@ class Graph } private: - bool unit_test_mode_; + bool unit_test_mode_ = false; bool insts_placed_ = false; uint32_t num_loops_ = 0; Loop *root_loop_ = nullptr; std::string name_method_; std::vector all_inst_; std::vector all_regions_; + LiveInterval *live_intervals_ = nullptr; + LinearNumber num_linear_inst_ = 0; }; } diff --git a/src/inst.cpp b/src/inst.cpp index a088d40..865bf7f 100644 --- a/src/inst.cpp +++ b/src/inst.cpp @@ -255,4 +255,9 @@ RegionInst *Inst::CastToRegion() { return static_cast(this); } +IfInst *Inst::CastToIf() { + ASSERT(GetOpcode() == Opcode::If); + return static_cast(this); +} + } diff --git a/src/inst.h b/src/inst.h index 9ddff7f..712a76b 100644 --- a/src/inst.h +++ b/src/inst.h @@ -43,6 +43,7 @@ std::string CcToString(ConditionCode cc); std::string TypeToString(Type type); class RegionInst; +class IfInst; class Inst { @@ -121,7 +122,7 @@ class Inst virtual void DumpUsers(std::ostream &out); uint32_t NumDataUsers(); - std::list &GetRawUsers() { + std::list & GetRawUsers() { return users_; } @@ -137,10 +138,14 @@ class Inst return; } - bool IsRegion() { + bool IsRegion() const { return GetOpcode() == Opcode::Start || GetOpcode() == Opcode::Region || GetOpcode() == Opcode::End; } + bool IsPhi() const { + return GetOpcode() == Opcode::Phi; + } + void SetPrev(Inst *inst) { prev_ = inst; } @@ -158,8 +163,9 @@ class Inst } RegionInst *CastToRegion(); + IfInst *CastToIf(); - bool IsPlaced() { + bool IsPlaced() const { return inst_placed_; } @@ -449,6 +455,21 @@ class RegionInst : public ControlProp return last_; } + id_t GetIndexPredecessor(Inst* inst) { + id_t index = 0; + id_t num_inputs_succ_region = NumDataInputs(); + for (index = 0; index < num_inputs_succ_region; index++) { + if (GetDataInput(index) == inst) { + return index; + } + } + UNREACHABLE(); + } + + LifeNumber GetLifeNumberEndOfRegion() { + return GetLast()->GetLifeNumber() + 2; + } + private: void AddFirstInst(Inst *inst); @@ -501,12 +522,12 @@ class IfInst : public ControlProp> GetRawUsers().push_back(nullptr); } - Inst *GetTrueBranch() { - return *(GetRawUsers().begin()); + RegionInst *GetTrueBranch() { + return (*(GetRawUsers().begin()))->CastToRegion(); } - Inst *GetFalseBranch() { - return *(++GetRawUsers().begin()); + RegionInst *GetFalseBranch() { + return (*(++GetRawUsers().begin()))->CastToRegion(); } void SetTrueBranch(Inst *inst) { diff --git a/src/optimizations/analysis/linear_order.cpp b/src/optimizations/analysis/linear_order.cpp index e6f9133..e592858 100644 --- a/src/optimizations/analysis/linear_order.cpp +++ b/src/optimizations/analysis/linear_order.cpp @@ -1,18 +1,86 @@ #include "linear_order.h" #include "rpo.h" +#include "analysis.h" +#include "loop_analysis.h" namespace compiler { void LinearOrder::Run() { - // TODO: Fix temporary solution - auto rpo = RpoRegions(graph_); - rpo.Run(); - linear_order_ = rpo.GetVector(); + if (!graph_->IsInstsPlaced()) { + UNREACHABLE(); + } + + LoopAnalysis(graph_).Run(); + RecursiveOrder(graph_->GetStartRegion()); } std::vector &LinearOrder::GetVector() { return linear_order_; } +void LinearOrder::RecursiveOrder(RegionInst *region) { + if (marker_.IsMarked(region)) { + return; + } + // If Region is loop header + if (region->IsLoopHeader() && !region->GetLoop()->IsIrreducible()) { + // Two case in this if: + // 1. If loop is reduceble, check preheaders is visited + // 2. If loop is irreduceble, don't check something + if (!region->GetLoop()->IsIrreducible() && !AllPreheadersIsVisited(region)) { + return; + } + } else { + if (!AllPrevIsVisited(region)) { + return; + } + } + + linear_order_.push_back(region); + marker_.SetMarker(region); + + if (region->GetOpcode() == Opcode::End) { + return; + } + + auto last_inst = region->GetLast(); + if (last_inst->GetOpcode() == Opcode::Jump) { + RecursiveOrder(last_inst->GetControlUser()->CastToRegion()); + return; + } + ASSERT(last_inst->GetOpcode() == Opcode::If); + // TODO Change condition in IfInst, if false is jump back (region is already visited) + auto if_inst = last_inst->CastToIf(); + RecursiveOrder(if_inst->GetFalseBranch()); + RecursiveOrder(if_inst->GetTrueBranch()); +} + +void LinearOrder::AddRegionToOrder(RegionInst *region) { + linear_order_.push_back(region); +} + +bool LinearOrder::AllPrevIsVisited(RegionInst *region) { + for (id_t i = 0; i < region->NumAllInputs(); i++) { + auto prev_region = GetRegionByInputRegion(region->GetRegionInput(i)); + if (!marker_.IsMarked(prev_region)) { + return false; + } + } + return true; +} + +bool LinearOrder::AllPreheadersIsVisited(RegionInst *region) { + auto backedges = region->GetLoop()->GetBackedges(); + for (id_t i = 0; i < region->NumAllInputs(); i++) { + auto prev_region = GetRegionByInputRegion(region->GetRegionInput(i)); + if (std::find(backedges.begin(), backedges.end(), prev_region) != backedges.end()) { + continue; + } + if (!marker_.IsMarked(prev_region)) { + return false; + } + } + return true; +} } diff --git a/src/optimizations/analysis/linear_order.h b/src/optimizations/analysis/linear_order.h index c62c22e..140cc31 100644 --- a/src/optimizations/analysis/linear_order.h +++ b/src/optimizations/analysis/linear_order.h @@ -1,6 +1,7 @@ #pragma once #include "inst.h" +#include "marker.h" namespace compiler { @@ -9,14 +10,22 @@ class Graph; class LinearOrder { public: LinearOrder(Graph *graph): - graph_(graph) {}; + graph_(graph), + marker_(graph) {}; void Run(); std::vector &GetVector(); +private: + void RecursiveOrder(RegionInst *region); + void AddRegionToOrder(RegionInst *region); + bool AllPrevIsVisited(RegionInst *region); + bool AllPreheadersIsVisited(RegionInst *region); + private: Graph *graph_; + Marker marker_; std::vector linear_order_; }; diff --git a/src/optimizations/analysis/liveness_analyzer.cpp b/src/optimizations/analysis/liveness_analyzer.cpp index 63a54bf..a590668 100644 --- a/src/optimizations/analysis/liveness_analyzer.cpp +++ b/src/optimizations/analysis/liveness_analyzer.cpp @@ -1,6 +1,7 @@ #include "liveness_analyzer.h" #include "linear_order.h" #include "graph.h" +#include "loop_analysis.h" namespace compiler { @@ -8,14 +9,14 @@ void LivenessAnalyzer::Run() { ASSERT(graph_->IsInstsPlaced()); PrepareData(); BuildLifeNumbers(); - - DumpLifeLinearData(std::cerr); + BuildIntervals(); } void LivenessAnalyzer::PrepareData() { + // TODO RunPass to eluminate redundant analysis auto lo = LinearOrder(graph_); lo.Run(); - // Here will be copying of vector + // Here is copying of vector linear_regions_ = lo.GetVector(); auto size = linear_regions_.size(); @@ -27,44 +28,140 @@ void LivenessAnalyzer::BuildLifeNumbers() { LinearNumber linear_number = 0; for (auto region : linear_regions_) { - LifeNumber start_block = life_number; - region->SetLifeNumber(start_block); + FillLifeNumbersInRegionBlock(region, life_number, linear_number); + } + num_linear_inst_ = linear_number; +} - for (Inst *inst = region->GetFirst(); inst != nullptr; inst = inst->GetNext()) { - inst->SetLinearNumber(linear_number++); - if (inst->GetOpcode() != Opcode::Phi) { - life_number += 2; // +2 reserved for create spill-fill inst. It isn't necessary for Phi inst - } +void LivenessAnalyzer::FillLifeNumbersInRegionBlock(RegionInst *region, LifeNumber &life_number, LinearNumber &linear_number) { + LifeNumber start_block = life_number; + region->SetLifeNumber(start_block); + for (Inst *inst = region->GetFirst(); inst != nullptr; inst = inst->GetNext()) { + if (inst->GetOpcode() == Opcode::Jump) { inst->SetLifeNumber(life_number); + break; // It is last inst in RegionBlock + } + inst->SetLinearNumber(linear_number++); + if (inst->GetOpcode() != Opcode::Phi) { + life_number += 2; // +2 reserved for create spill-fill inst. It isn't necessary for Phi inst } - BuildLifeIfJump(region, linear_number, life_number); - SetRegionLiveRanges(region, LiveRange(start_block, life_number)); + inst->SetLifeNumber(life_number); + } + // LifeNumber of the end RegionBlock is more on 2 than value LifeNumber of the last instruction + if (region->GetOpcode() != Opcode::End) { + life_number += 2; } + SetRegionLiveRanges(region, LiveRange(start_block, life_number)); } void LivenessAnalyzer::SetRegionLiveRanges(RegionInst *region, LiveRange &&range) { region_live_ranges_[region->GetId()] = range; } +void LivenessAnalyzer::BuildIntervals() { + live_intervals_.resize(num_linear_inst_); + for (LinearNumber i = 0; i < num_linear_inst_; i++) { + live_intervals_[i].SetLinearNumber(i); + } + + for (auto it = linear_regions_.rbegin(); it != linear_regions_.rend(); it++) { + auto region = *it; + auto live_set = new RegionBlockLiveSet(num_linear_inst_); + region_block_livesets_[region->GetId()] = live_set; + + // It should be first iteration + if (region->GetOpcode() == Opcode::End) { + continue; + } + CalcIniteialLiveSet(region); + ReverseIterateRegionBlock(region); + ProcessHeaderRegion(region); + } +} + +void LivenessAnalyzer::ProcessHeaderRegion(RegionInst *region) { + if (!region->IsLoopHeader()) { + return; + } + auto loop = region->GetLoop(); + LifeNumber min_lifenumber = region->GetLifeNumber(); + LifeNumber max_lifenumber = region->GetLifeNumber(); + for (auto body_loop : loop->GetBody()) { + max_lifenumber = std::max(max_lifenumber, body_loop->GetLifeNumberEndOfRegion()); + } + UpdateLiveIntervalAllLiveSet(region_block_livesets_[region->GetId()], min_lifenumber, max_lifenumber + 2); +} + +void LivenessAnalyzer::CalcIniteialLiveSet(RegionInst *region) { + for (auto succ_region : region->GetLast()->GetRawUsers()) { + if (region_block_livesets_[succ_region->GetId()] == nullptr) { + continue; + } + + region_block_livesets_[region->GetId()]->Copy(region_block_livesets_[succ_region->GetId()]); + // TODO Check work with many phi + + if (succ_region->GetOpcode() == Opcode::End) { + continue; + } + for (Inst *i = succ_region->GetControlUser(); i->GetOpcode() == Opcode::Phi; i = i->GetNext()) { + PhiInst *phi_inst = static_cast(i); + uint32_t index_pred = succ_region->CastToRegion()->GetIndexPredecessor(region->GetLast()); + region_block_livesets_[region->GetId()]->Set(phi_inst->GetDataInput(index_pred)->GetLinearNumber()); + } + } + UpdateLiveIntervalAllLiveSet(region_block_livesets_[region->GetId()], region->GetLifeNumber(), region->GetLast()->GetLifeNumber()); +} + +void LivenessAnalyzer::ReverseIterateRegionBlock(RegionInst *region) { + auto live_set = region_block_livesets_[region->GetId()]; + for (Inst *inst = region->GetLast(); inst != nullptr; inst = inst->GetPrev()) { + // Is looking instruction + if (IsInstWithoutLife(inst)) { + continue; + } + auto inst_linear_number = inst->GetLinearNumber(); + // Return has life_number and linear number, but doesn't have the live interval + if (HaveLifeInterval(inst)) { + live_intervals_[inst_linear_number].TrimBegin(inst->GetLifeNumber()); + } + live_set->Clear(inst_linear_number); + + // Are looking inputs + if (inst->GetOpcode() == Opcode::Constant || inst->GetOpcode() == Opcode::Parameter) { + continue; + } + auto num_inputs = inst->NumDataInputs(); + for (id_t i = 0; i < num_inputs; i++) { + auto new_inst = inst->GetDataInput(i); + live_intervals_[new_inst->GetLinearNumber()].Append(region->GetLifeNumber(), inst->GetLifeNumber()); + live_set->Set(new_inst->GetLinearNumber()); + } + } + // TODO Do more intuitive API for work with Phi + for (Inst *phi = region->GetControlUser(); phi->IsPhi(); phi = phi->GetControlUser()) { + live_set->Clear(phi->GetLinearNumber()); + } +} + + void LivenessAnalyzer::DumpLifeLinearData(std::ostream &out) { bool first = true; for (auto region : linear_regions_) { if (first) { out << "----------------------------\n"; } + out << "BB begin life:" << region->GetLifeNumber() << std::endl; region->Dump(out); - PrintLifeLinearData(region, out); + for (Inst *inst = region->GetFirst(); inst != nullptr; inst = inst->GetNext()) { inst->Dump(out); PrintLifeLinearData(inst, out); } + if (region->GetOpcode() != Opcode::End) { - ASSERT(region->GetLast()); - auto inst = region->GetLast(); - inst->Dump(out); - if (inst->GetOpcode() != Opcode::Jump) { - PrintLifeLinearData(inst, out); - } + out << "Block live range: [" << region_live_ranges_[region->GetId()].GetBegin() << ", " + << region_live_ranges_[region->GetId()].GetEnd() << ")" << std::endl; } out << "----------------------------\n"; first = false; @@ -72,12 +169,12 @@ void LivenessAnalyzer::DumpLifeLinearData(std::ostream &out) { } void LivenessAnalyzer::PrintLifeLinearData(Inst *inst, std::ostream &out) { - out << "life: " << inst->GetLifeNumber(); auto opc = inst->GetOpcode(); if (!inst->IsRegion() && opc != Opcode::Jump) { + out << " life: " << inst->GetLifeNumber(); out <<" lin: " << inst->GetLinearNumber(); + out << "\n"; } - out << "\n"; } void LivenessAnalyzer::BuildLifeIfJump(RegionInst *region, LinearNumber &linear_number, LifeNumber &life_number) { @@ -93,4 +190,19 @@ void LivenessAnalyzer::BuildLifeIfJump(RegionInst *region, LinearNumber &linear_ region->GetLast()->SetLifeNumber(life_number); } +void LivenessAnalyzer::UpdateLiveIntervalAllLiveSet(RegionBlockLiveSet* live_set, LifeNumber begin, LifeNumber end) { + for (LinearNumber i = 0; i < live_set->Size(); i++) { + if (live_set->IsSet(i)) { + live_intervals_[i].Append(begin, end); + } + } +} + +void LivenessAnalyzer::DumpIntervals() { + for (LinearNumber i = 0; i < num_linear_inst_; i++) { + auto live_interval = live_intervals_[i]; + std::cout << "Interval:" << i << " [" << live_interval.GetBegin() << ", " << live_interval.GetEnd() << ")" << std::endl; + } +} + } diff --git a/src/optimizations/analysis/liveness_analyzer.h b/src/optimizations/analysis/liveness_analyzer.h index 86f2248..e81901c 100644 --- a/src/optimizations/analysis/liveness_analyzer.h +++ b/src/optimizations/analysis/liveness_analyzer.h @@ -3,7 +3,6 @@ #include #include "inst.h" - namespace compiler { class Graph; @@ -16,33 +15,202 @@ class LiveRange { LiveRange(): begin_(0), end_(0) {}; -private: + LifeNumber GetBegin() const { + return begin_; + } + + LifeNumber GetEnd() const { + return end_; + } + + +protected: LifeNumber begin_; LifeNumber end_; }; +class LiveInterval : public LiveRange { +public: + LiveInterval(): + LiveRange() {}; + + LiveInterval(LifeNumber begin, LifeNumber end): + LiveRange(begin, end) {}; + + void Append(LifeNumber begin, LifeNumber end) { + if (is_first_) { + begin_ = begin; + end_ = end; + is_first_ = false; + return; + } + + // OldBegin OldEnd + // ||-----------|| + // ||-----------------|| + // begin end + // ==============RESULT============== + // ||---------------------------|| + // NewBegin NewEnd + if (begin < begin_ && end < end_) { + begin_ = begin; + return; + } + + // OldBegin OldEnd + // ||-----------|| + // ||--------------------------|| + // begin end + // ==============RESULT============== + // ||--------------------------|| + // NewBegin NewEnd + if (begin < begin_ && end > end_) { + begin_ = begin; + end_ = end; + return; + } + + // OldBegin OldEnd + // ||--------------------------|| + // ||-----------|| + // begin end + // ==============RESULT============== + // ||--------------------------|| + // NewBegin NewEnd + if (begin > begin_ && end < end_) { + return; + } + + // OldBegin OldEnd + // ||---------|| + // ||-----------|| + // begin end + // ==============RESULT============== + // ||---------------------|| + // NewBegin NewEnd + if (begin > begin_ && end > end_) { + return; + } + + UNREACHABLE(); + } + + void TrimBegin(LifeNumber begin) { + ASSERT(begin >= begin_); + begin_ = begin; + } + + void Dump() { + std::cerr << "[" << begin_ << ", " << end_ << ")" << std::endl; + } + + void SetLinearNumber(LinearNumber number) { + my_linear_number_ = number; + } + + LinearNumber GetLinearNumber() const { + return my_linear_number_; + } + +private: + bool is_first_ = true; + LinearNumber my_linear_number_; +}; + +class RegionBlockLiveSet { +public: + RegionBlockLiveSet(LifeNumber size): + live_inst_(size, false), + size_(size) {} + + RegionBlockLiveSet() = delete; + + void Set(LinearNumber index) { + ASSERT(index < live_inst_.size()); + live_inst_[index] = true; + } + + void Clear(LinearNumber index) { + ASSERT(index < live_inst_.size()); + live_inst_[index] = false; + } + + bool IsSet(LinearNumber index) { + ASSERT(index < live_inst_.size()); + return live_inst_[index]; + } + + void Copy(RegionBlockLiveSet *live_set) { + ASSERT(live_set->size_ == size_); + for (LinearNumber i = 0; i < size_; i++) { + live_inst_[i] = live_set->live_inst_[i]; + } + } + + LinearNumber Size() { + return size_; + } + +private: + // std::vector possible have optimal realisation for bool + std::vector live_inst_; + LinearNumber size_ = 0; +}; + class LivenessAnalyzer { public: LivenessAnalyzer(Graph *graph): graph_(graph) {}; - void Run(); + ~LivenessAnalyzer() { + for (auto liveset : region_block_livesets_) { + delete liveset.second; + } + } + void Run(); void DumpLifeLinearData(std::ostream &out); + std::vector& GetLiveIntervals() { + return live_intervals_; + } private: void PrepareData(); void BuildLifeNumbers(); + void FillLifeNumbersInRegionBlock(RegionInst *region, LifeNumber &life_number, LinearNumber &linear_number); void SetRegionLiveRanges(RegionInst *region, LiveRange &&range); void PrintLifeLinearData(Inst *inst, std::ostream &out); void BuildLifeIfJump(RegionInst *region, LinearNumber &linear_number, LifeNumber &life_number); + void BuildIntervals(); + void CalcIniteialLiveSet(RegionInst *region); + void UpdateLiveIntervalAllLiveSet(RegionBlockLiveSet* live_set, LifeNumber begin, LifeNumber end); + void ReverseIterateRegionBlock(RegionInst *region); + void ProcessHeaderRegion(RegionInst *region); + void DumpIntervals(); + + bool IsInstWithoutLife(Inst *inst) { // Oh my, instruction don't have life. It is sad... :( + auto opc = inst->GetOpcode(); + return opc == Opcode::Jump; + } + + bool HaveLifeInterval(Inst *inst) { + auto opc = inst->GetOpcode(); + // Exception is instructions which have life_number and linear number, but doesn't have the live interval + return !(opc == Opcode::If || opc == Opcode::Return); + } + + private: + LinearNumber num_linear_inst_ = 0; Graph *graph_; std::vector linear_regions_; std::vector inst_life_numbers_; + std::vector live_intervals_; + // Unfortunately, I had to use a "map", since in Sea Of Nodes the region indices are not in order std::map region_live_ranges_; + std::map region_block_livesets_; }; } diff --git a/src/optimizations/gcm.cpp b/src/optimizations/gcm.cpp index 0201c50..2e87615 100644 --- a/src/optimizations/gcm.cpp +++ b/src/optimizations/gcm.cpp @@ -57,8 +57,8 @@ void GCM::PlacingExitFromRegion(Inst *inst, RegionInst *region) { if (opc == Opcode::If) { // TODO: Find more good place for this fill - region->PushBackInst(inst); PlacingDataInst(inst->GetDataInput(0), region); + region->PushBackInst(inst); return; } } diff --git a/src/optimizations/linear_scan.cpp b/src/optimizations/linear_scan.cpp new file mode 100644 index 0000000..4e6fe8a --- /dev/null +++ b/src/optimizations/linear_scan.cpp @@ -0,0 +1,78 @@ +#include "linear_scan.h" +#include "analysis/liveness_analyzer.h" + +namespace compiler { + + +void LinearScanRegAlloc::Run() { + for (LinearNumber index = 0; index < regs_map_sorted_begin_.size(); index++) { + auto &map = regs_map_sorted_begin_[index]; + if (map.interval.GetBegin() == map.interval.GetEnd() && map.interval.GetBegin() == 0) { + continue; + } + ExpireOldIntervals(map); + + if (free_regs_.empty()) { + SpillAtInterval(map, index); + } else { + map.location = free_regs_.back(); + free_regs_.pop_back(); + action_idx_sorted_end_.push_back(index); + SortActionIndexes(); + } + } +} + +void LinearScanRegAlloc::ExpireOldIntervals([[maybe_unused]]RegMap &map) { + for (auto it = action_idx_sorted_end_.begin(); it != action_idx_sorted_end_.end(); it = action_idx_sorted_end_.begin()) { + // Yes, it is look strage + if (regs_map_sorted_begin_[*it].interval.GetEnd() > map.interval.GetBegin()) { + return; + } + if (map.location.is_reg) { + free_regs_.push_back(map.location); + } + action_idx_sorted_end_.pop_front(); + } +} + +void LinearScanRegAlloc::SpillAtInterval(RegMap &map, LinearNumber index) { + if (regs_map_sorted_begin_[action_idx_sorted_end_.back()].interval.GetEnd() > map.interval.GetEnd()) { + map.location = regs_map_sorted_begin_[action_idx_sorted_end_.back()].location; + if (free_stack_location_.empty()) { + AddNewStackLocation(); + } + regs_map_sorted_begin_[action_idx_sorted_end_.back()].location = free_stack_location_.back(); + free_stack_location_.pop_back(); + + // Remove spilled from active + action_idx_sorted_end_.pop_back(); + action_idx_sorted_end_.push_back(index); + SortActionIndexes(); + } else { + if (free_stack_location_.empty()) { + AddNewStackLocation(); + } + map.location = free_stack_location_.back(); + free_stack_location_.pop_back(); + } + +} + +void LinearScanRegAlloc::AddNewStackLocation() { + num_stack_locations_++; + free_stack_location_.push_back(StackLocation(num_stack_locations_, std::string("s") + std::to_string(num_stack_locations_))); +} + +void LinearScanRegAlloc::SortActionIndexes() { + action_idx_sorted_end_.sort([this](RegMapLinkIndex &left, RegMapLinkIndex &right) + {return regs_map_sorted_begin_[left].interval.GetEnd() < regs_map_sorted_begin_[right].interval.GetEnd();}); +} + +void LinearScanRegAlloc::Dump() { + for (auto map : regs_map_sorted_begin_) { + std::cerr << "LinNum: " << map.interval.GetLinearNumber() << "(" << map.location.name << ")" << std::endl; + } +} + +} diff --git a/src/optimizations/linear_scan.h b/src/optimizations/linear_scan.h new file mode 100644 index 0000000..0ef8ad5 --- /dev/null +++ b/src/optimizations/linear_scan.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include "analysis/liveness_analyzer.h" + +namespace compiler { + +struct LocationData { + LocationData(int32_t reg_index, std::string&& reg_name, bool it_is_reg): + index(reg_index), + name(reg_name), + is_reg(it_is_reg) {}; + + int32_t index; + std::string name; + bool is_reg; +}; + +struct Register: public LocationData { + Register(int32_t reg_index, std::string&& reg_name): + LocationData(reg_index, std::move(reg_name), true) {}; + + Register(LocationData &location): + LocationData(location) { + ASSERT(location.is_reg); + }; +}; + +struct StackLocation: public LocationData { + StackLocation(int32_t reg_index, std::string&& reg_name): + LocationData(reg_index, std::move(reg_name), false) {}; + + StackLocation(LocationData &location): + LocationData(location) { + ASSERT(!location.is_reg); + }; +}; + +struct RegMap { + LiveInterval interval; + LocationData location; +}; + +using RegMapLinkIndex = LinearNumber; + +class LinearScanRegAlloc { +public: + LinearScanRegAlloc(std::vector& intervals): + live_intervals_(intervals) { + + auto num_linear_inst = intervals.size(); + regs_map_sorted_begin_.reserve(num_linear_inst); + for (LinearNumber j = 0; j < num_linear_inst; j++) { + regs_map_sorted_begin_.push_back({intervals[j], LocationData(-1, "NOT SET", false)}); + } + std::sort(regs_map_sorted_begin_.begin(), regs_map_sorted_begin_.end(), + [](RegMap &left, RegMap &right) {return left.interval.GetBegin() < right.interval.GetBegin();}); + + for (uint32_t i = 0; i < num_regs_; i++) { + free_regs_.push_back(Register(i, std::string("x") + std::to_string(i))); + } + } + + std::vector& GetRegsMap() { + return regs_map_sorted_begin_; + } + + void Run(); + +private: + void ExpireOldIntervals(RegMap &map); + void SpillAtInterval(RegMap &map, LinearNumber index); + void AddNewStackLocation(); + void SortActionIndexes(); + void Dump(); + +private: + std::list free_regs_; + uint32_t num_regs_ = 3; + uint32_t num_stack_locations_ = 0; + std::list free_stack_location_; + std::vector live_intervals_; + std::vector regs_map_sorted_begin_; + std::list action_idx_sorted_end_; +}; + +} diff --git a/src/pass_manager.h b/src/pass_manager.h new file mode 100644 index 0000000..6d057ad --- /dev/null +++ b/src/pass_manager.h @@ -0,0 +1,5 @@ +#pragma once + +namespace compiler { + +} \ No newline at end of file diff --git a/tests/analysis_tests.cpp b/tests/analysis_tests.cpp index 06df3a2..72a1931 100644 --- a/tests/analysis_tests.cpp +++ b/tests/analysis_tests.cpp @@ -11,6 +11,7 @@ #include "optimizations/gcm.h" #include "optimizations/analysis/linear_order.h" #include "optimizations/analysis/liveness_analyzer.h" +#include "optimizations/linear_scan.h" namespace compiler { @@ -38,6 +39,21 @@ void CheckOrderPlacedInsts(Graph *graph, id_t index_region, std::vector or ASSERT_EQ(num, order.size()); } +void CheckLocationData(std::vector ®s_map, LinearNumber linear_number, LocationData location) { + auto find_map = std::find_if(regs_map.begin(), regs_map.end(), [linear_number](RegMap& map) { return map.interval.GetLinearNumber() == linear_number; }); + ASSERT(find_map != regs_map.end()); + ASSERT_EQ((*find_map).location.is_reg, location.is_reg); + ASSERT_EQ((*find_map).location.index, location.index); +} + +#define CHECK_LIVE_INTERVAL_INST(/* Graph* */ graph, /* std::vector */ all_live_intervals, /* id_t */ idx_inst, /* LiveInterval */ true_live_interval) \ +{ \ + auto inst = (graph)->GetInstByIndex((idx_inst)); \ + auto li = (all_live_intervals)[inst->GetLinearNumber()]; \ + ASSERT_EQ(li.GetBegin(), (true_live_interval).GetBegin()); \ + ASSERT_EQ(li.GetEnd(), (true_live_interval).GetEnd()); \ +} + /* * 0.Start * | @@ -698,11 +714,102 @@ TEST(GcmTest, GcmTest2) { GCM(graph).Run(); CheckOrderPlacedInsts(graph, 0, {3, 2, 8, 10}); - CheckOrderPlacedInsts(graph, 7, {9, 5, 4}); + CheckOrderPlacedInsts(graph, 7, {9, 4, 5}); CheckOrderPlacedInsts(graph, 6, {11}); CheckOrderPlacedInsts(graph, 1, {}); } +TEST(GcmTest, GcmTest3) { + auto ic = IrConstructor(); + ic.CreateInst(0); + ic.CreateInst(2); + ic.CreateInst(3).Imm(0); + ic.CreateInst(4).DataInputs(2, 3).CC(ConditionCode::EQ); + ic.CreateInst(5).CtrlInput(0).DataInputs(4).Branches(9, 6); + + ic.CreateInst(6); + ic.CreateInst(7).Imm(1); + ic.CreateInst(8).CtrlInput(6).JmpTo(9); + + + ic.CreateInst(9); + ic.CreateInst(10).CtrlInput(9).DataInputs(3, 7); + ic.CreateInst(11).CtrlInput(10).DataInputs(10); + ic.CreateInst(12).CtrlInput(11).JmpTo(1); + + ic.CreateInst(1); + + auto graph = ic.GetFinalGraph(); + GCM(graph).Run(); + + CheckOrderPlacedInsts(graph, 0, {7, 3, 2, 4, 5}); + CheckOrderPlacedInsts(graph, 6, {8}); + CheckOrderPlacedInsts(graph, 9, {10, 11, 12}); + CheckOrderPlacedInsts(graph, 1, {}); +} + +/* True linear order of graph in test below + + +--------------------------------------------+ + | 0. Start -> v2 | + | 5.i64 Constant 0x0 -> v8, v10, v15, v20 | + | 2. Jump v0 -> v3 | + +-----------+--------------------------------+ + | + v + +----------------------------------+ + | 3. Region v2, v22 -> v4 |<-----------------+ + | 4. Jump v3 -> v6 | | + +-----------+----------------------+ | + | | + v | + +------------------------------------------+ | + T | 6. Region v4, v20 -> v8 |<----+ | ++------+ 8. If v6, v5 -> T:v9, F:v12 | | | +| +-----------+------------------------------+ | | +| |F | | +| v | | +| +------------------------------+ | | +| | 12. Region v8 -> v13 | | | +| | 13. Jump v12 -> v17 | | | +| +-----------+------------------+ | | +| | | | +| v | | +| +------------------------------------------+ | | ++----->| 9. Region v8 -> v10 | | | + T | 10. If v9, v5 -> T:v14, F:v17 | | | ++------+-----------+------------------------------+ | | +| |F | | +| v | | +| +-----------------------------------+ | | +| | 17. Region v13, v10 -> v18 | | | +| | 18. Jump v17 -> v19 | | | +| +-----------+-----------------------+ | | +| | | | +| v | | +| +------------------------------------------+ | | +| | 19. Region v18 -> v20 |F | | +| | 20. If v19, v5 -> T:v21, F:v6 +-----+ | +| +-----------+------------------------------+ | +| |T | +| v | +| +------------------------------+ | +| | 21. Region v20 -> v22 | | +| | 22. Jump v21 -> v3 +----------------------+ +| +-----------+------------------+ +| | +| v +| +----------------------------------+ ++----->| 14. Region v10 -> v15 | + | 15. Return v14, v5 -> v16 | + | 16. Jump v15 -> v1 | + +-----------+----------------------+ + | + v + +-----------------------+ + | 1. End v16 | + +-----------------------+ + */ TEST(LinearOrder, LinearOrder1) { auto ic = IrConstructor(); ic.CreateInst(5).Imm(0); @@ -741,7 +848,6 @@ TEST(LinearOrder, LinearOrder1) { auto graph = ic.GetFinalGraph(); GCM(graph).Run(); - graph->Dump(std::cerr); auto lo = LinearOrder(graph); lo.Run(); @@ -753,7 +859,28 @@ TEST(LinearOrder, LinearOrder1) { ASSERT_EQ(lo.GetVector()[i]->GetId(), true_linear_order[i]); } } - +/* + ---------------------------- + BB begin life:0 + 0. Start [Loop:root] -> v10 + 2.i64 Constant 0x7b -> v4, v5, v6, v7 + life: 2 lin: 0 Alive: [2, 6) <<<<================ + 10. Jump v0 -> v3 + Block live range: [0, 4) + ---------------------------- + BB begin life:4 + 3. Region [Loop:root] v10 -> v8 + 4. Add v2, v2 -> v8 + life: 6 lin: 1 Alive: [6, 8) <<<<================ + 8. Return v3, v4 -> v9 + life: 8 lin: 2 Alive: [0, 0) <<<<================ + 9. Jump v8 -> v1 + Block live range: [4, 10) + ---------------------------- + BB begin life:10 + 1. End [Loop:root] v9 + ---------------------------- + */ TEST(LivenessAnalyzerTest, Test1) { auto ic = IrConstructor(); ic.CreateInst(0); @@ -775,7 +902,224 @@ TEST(LivenessAnalyzerTest, Test1) { GCM(graph).Run(); auto la = LivenessAnalyzer(graph); la.Run(); - graph->Dump(std::cerr); + + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 2, LiveInterval(2, 6)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 4, LiveInterval(6, 8)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 8, LiveInterval(0, 0)); +} + + +/* + ---------------------------- + BB begin life:0 + 0. Start [Loop:root] -> v2 + 5.i64 Constant 0x0 -> v8, v10, v15, v20 + life: 2 lin: 0 Alive: [2, 26) <<<<================ + 2. Jump v0 -> v3 + Block live range: [0, 4) + ---------------------------- + BB begin life:4 + 3. Region [Loop:1, Depth:1, Header] v2, v22 -> v4 + 4. Jump v3 -> v6 + Block live range: [4, 6) + ---------------------------- + BB begin life:6 + 6. Region [Loop:2, Depth:2, Header] v4, v20 -> v8 + 8. If v6, v5 -> T:v9, F:v12 + life: 8 lin: 1 Alive: [0, 0) <<<<================ + Block live range: [6, 10) + ---------------------------- + BB begin life:10 + 12. Region [Loop:2, Depth:2] v8 -> v13 + 13. Jump v12 -> v17 + Block live range: [10, 12) + ---------------------------- + BB begin life:12 + 9. Region [Loop:2, Depth:2] v8 -> v10 + 10. If v9, v5 -> T:v14, F:v17 + life: 14 lin: 2 Alive: [0, 0) <<<<================ + Block live range: [12, 16) + ---------------------------- + BB begin life:16 + 17. Region [Loop:2, Depth:2] v13, v10 -> v18 + 18. Jump v17 -> v19 + Block live range: [16, 18) + ---------------------------- + BB begin life:18 + 19. Region [Loop:2, Depth:2, Backedge] v18 -> v20 + 20. If v19, v5 -> T:v21, F:v6 + life: 20 lin: 3 Alive: [0, 0) <<<<================ + Block live range: [18, 22) + ---------------------------- + BB begin life:22 + 21. Region [Loop:1, Depth:1, Backedge] v20 -> v22 + 22. Jump v21 -> v3 + Block live range: [22, 24) + ---------------------------- + BB begin life:24 + 14. Region [Loop:root] v10 -> v15 + 15. Return v14, v5 -> v16 + life: 26 lin: 4 Alive: [0, 0) <<<<================ + 16. Jump v15 -> v1 + Block live range: [24, 28) + ---------------------------- + BB begin life:28 + 1. End [Loop:root] v16 + ---------------------------- + */ +TEST(LivenessAnalyzerTest, Test2) { + auto ic = IrConstructor(); + ic.CreateInst(5).Imm(0); + + ic.CreateInst(0); + ic.CreateInst(2).CtrlInput(0).JmpTo(3); + + ic.CreateInst(3); + ic.CreateInst(4).CtrlInput(3).JmpTo(6); + + ic.CreateInst(6); + ic.CreateInst(8).CtrlInput(6).DataInputs(5).Branches(9, 12); + + ic.CreateInst(9); + ic.CreateInst(10).CtrlInput(9).DataInputs(5).Branches(14, 17); + + ic.CreateInst(14); + ic.CreateInst(15).DataInputs(5).CtrlInput(14); + ic.CreateInst(16).CtrlInput(15).JmpTo(1); + + ic.CreateInst(12); + ic.CreateInst(13).CtrlInput(12).JmpTo(17); + + ic.CreateInst(17); + ic.CreateInst(18).CtrlInput(17).JmpTo(19); + + + ic.CreateInst(19); + ic.CreateInst(20).CtrlInput(19).DataInputs(5).Branches(21, 6); + + ic.CreateInst(21); + ic.CreateInst(22).CtrlInput(21).JmpTo(3); + + ic.CreateInst(1); + + auto graph = ic.GetFinalGraph(); + + GCM(graph).Run(); + auto la = LivenessAnalyzer(graph); + la.Run(); + + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 5, LiveInterval(2, 26)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 8, LiveInterval(0, 0)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 10, LiveInterval(0, 0)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 20, LiveInterval(0, 0)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 15, LiveInterval(0, 0)); +} + +/* + ---------------------------- + BB begin life:0 + 0. Start [Loop:root] -> v5 + 7.i64 Constant 0x1 -> v10 + life: 2 lin: 0 Alive: [2, 14) <<<<================ + 3.i64 Constant 0x0 -> v4, v10 + life: 4 lin: 1 Alive: [4, 14) <<<<================ + 2. Parameter -> v4 + life: 6 lin: 2 Alive: [6, 8) + 4.b Compare EQ v2, v3 -> v5 + life: 8 lin: 3 Alive: [8, 10) <<<<================ + 5. If v0, v4 -> T:v9, F:v6 + life: 10 lin: 4 Alive: [0, 0) <<<<================ + Block live range: [0, 12) + ---------------------------- + BB begin life:12 + 6. Region [Loop:root] v5 -> v8 + 8. Jump v6 -> v9 + Block live range: [12, 14) + ---------------------------- + BB begin life:14 + 9. Region [Loop:root] v8, v5 -> v10 + 10. Phi v9, v3(R8), v7(R5) -> v11, v11 + life: 14 lin: 5 Alive: [14, 16) <<<<================ + 11. Return v10, v10 -> v12 + life: 16 lin: 6 Alive: [0, 0) <<<<================ + 12. Jump v11 -> v1 + Block live range: [14, 18) + ---------------------------- + BB begin life:18 + 1. End [Loop:root] v12 + ---------------------------- + */ +TEST(LivenessAnalyzerTest, TestPhi) { + auto ic = IrConstructor(); + ic.CreateInst(0); + ic.CreateInst(2); + ic.CreateInst(3).Imm(0); + ic.CreateInst(4).DataInputs(2, 3).CC(ConditionCode::EQ); + ic.CreateInst(5).CtrlInput(0).DataInputs(4).Branches(9, 6); + + ic.CreateInst(6); + ic.CreateInst(7).Imm(1); + ic.CreateInst(8).CtrlInput(6).JmpTo(9); + + + ic.CreateInst(9); + ic.CreateInst(10).CtrlInput(9).DataInputs(3, 7); + ic.CreateInst(11).CtrlInput(10).DataInputs(10); + ic.CreateInst(12).CtrlInput(11).JmpTo(1); + + ic.CreateInst(1); + + auto graph = ic.GetFinalGraph(); + + GCM(graph).Run(); + auto la = LivenessAnalyzer(graph); + la.Run(); + + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 7, LiveInterval(2, 14)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 3, LiveInterval(4, 14)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 2, LiveInterval(6, 8)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 5, LiveInterval(0, 0)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 10, LiveInterval(14, 16)); + CHECK_LIVE_INTERVAL_INST(graph, la.GetLiveIntervals(), 11, LiveInterval(0, 0)); +} + +// Graph the same as TestPhi +TEST(LinearScanTest, Test1) { + auto ic = IrConstructor(); + ic.CreateInst(0); + ic.CreateInst(2); + ic.CreateInst(3).Imm(0); + ic.CreateInst(4).DataInputs(2, 3).CC(ConditionCode::EQ); + ic.CreateInst(5).CtrlInput(0).DataInputs(4).Branches(9, 6); + + ic.CreateInst(6); + ic.CreateInst(7).Imm(1); + ic.CreateInst(8).CtrlInput(6).JmpTo(9); + + + ic.CreateInst(9); + ic.CreateInst(10).CtrlInput(9).DataInputs(3, 7); + ic.CreateInst(11).CtrlInput(10).DataInputs(10); + ic.CreateInst(12).CtrlInput(11).JmpTo(1); + + ic.CreateInst(1); + + auto graph = ic.GetFinalGraph(); + + GCM(graph).Run(); + auto la = LivenessAnalyzer(graph); + la.Run(); + + auto ls = LinearScanRegAlloc(la.GetLiveIntervals()); + ls.Run(); + auto regs_map = ls.GetRegsMap(); + + CheckLocationData(regs_map, 0, Register(2, "")); + CheckLocationData(regs_map, 1, StackLocation(1, "")); + CheckLocationData(regs_map, 2, Register(0, "")); + CheckLocationData(regs_map, 4, LocationData(-1, "", false)); + CheckLocationData(regs_map, 5, StackLocation(2, "")); + CheckLocationData(regs_map, 6, LocationData(-1, "", false)); } }