/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/compiler/graph_linearizer.h" namespace panda::ecmascript::kungfu { void GraphLinearizer::Run(ControlFlowGraph &result) { LinearizeGraph(); LinearizeRegions(result); if (IsLogEnabled()) { LOG_COMPILER(INFO) << ""; LOG_COMPILER(INFO) << "\033[34m" << "====================" << " After graph linearizer " << "[" << GetMethodName() << "]" << "====================" << "\033[0m"; PrintGraph("Build Basic Block"); LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; } } class CFGBuilder { public: explicit CFGBuilder(GraphLinearizer *linearizer) : linearizer_(linearizer), pendingList_(linearizer->chunk_), endStateList_(linearizer->chunk_), acc_(linearizer->circuit_), scheduleLIR_(linearizer->IsSchedueLIR()) {} void Run() { VisitStateGates(); // connect region for (auto rootGate : linearizer_->regionRootList_) { auto toRegion = linearizer_->GateToRegion(rootGate); auto numStateIn = acc_.GetStateCount(rootGate); for (size_t i = 0; i < numStateIn; i++) { auto input = acc_.GetState(rootGate, i); ASSERT(acc_.IsState(input) || acc_.GetOpCode(input) == OpCode::STATE_ENTRY); auto fromRegion = linearizer_->FindPredRegion(input); fromRegion->AddSucc(toRegion); } } for (auto fixedGate : endStateList_) { auto state = acc_.GetState(fixedGate); auto region = linearizer_->FindPredRegion(state); linearizer_->AddFixedGateToRegion(fixedGate, region); linearizer_->BindGate(fixedGate, region); } } void VisitStateGates() { ASSERT(pendingList_.empty()); linearizer_->circuit_->AdvanceTime(); auto startGate = acc_.GetStateRoot(); acc_.SetMark(startGate, MarkCode::VISITED); pendingList_.emplace_back(startGate); bool litecg = linearizer_->enableLiteCG(); while (!pendingList_.empty()) { auto curGate = pendingList_.back(); pendingList_.pop_back(); VisitStateGate(curGate); if (acc_.GetOpCode(curGate) != OpCode::LOOP_BACK) { auto uses = acc_.Uses(curGate); // The LiteCG backend is unable to perform branch prediction scheduling, thus requiring advance // preparation if (litecg && acc_.GetOpCode(curGate) == OpCode::IF_BRANCH && acc_.HasBranchWeight(curGate)) { std::vector outs; acc_.GetOutStates(curGate, outs); GateRef bTrue = (acc_.GetOpCode(outs[0]) == OpCode::IF_TRUE) ? outs[0] : outs[1]; GateRef bFalse = (acc_.GetOpCode(outs[0]) == OpCode::IF_FALSE) ? outs[0] : outs[1]; if (acc_.GetTrueWeight(curGate) >= acc_.GetFalseWeight(curGate)) { std::swap(bTrue, bFalse); } if (acc_.GetMark(bTrue) == MarkCode::NO_MARK) { acc_.SetMark(bTrue, MarkCode::VISITED); pendingList_.emplace_back(bTrue); } if (acc_.GetMark(bFalse) == MarkCode::NO_MARK) { acc_.SetMark(bFalse, MarkCode::VISITED); pendingList_.emplace_back(bFalse); } continue; } for (auto useIt = uses.begin(); useIt != uses.end(); useIt++) { if (acc_.IsStateIn(useIt) && acc_.IsState(*useIt) && acc_.GetMark(*useIt) == MarkCode::NO_MARK) { acc_.SetMark(*useIt, MarkCode::VISITED); pendingList_.emplace_back(*useIt); } } } } } void VisitStateGate(GateRef gate) { if (scheduleLIR_) { linearizer_->CreateGateRegion(gate); } else { auto op = acc_.GetOpCode(gate); switch (op) { case OpCode::LOOP_BEGIN: case OpCode::MERGE: case OpCode::IF_TRUE: case OpCode::IF_FALSE: case OpCode::SWITCH_CASE: case OpCode::STATE_ENTRY: case OpCode::IF_EXCEPTION: case OpCode::IF_SUCCESS: { linearizer_->CreateGateRegion(gate); if (linearizer_->onlyBB_) { currentRegion_ = linearizer_->GateToRegion(gate); } break; } case OpCode::LOOP_BACK: case OpCode::IF_BRANCH: case OpCode::SWITCH_BRANCH: case OpCode::RETURN: case OpCode::RETURN_VOID: case OpCode::TYPED_CONDITION_JUMP: endStateList_.emplace_back(gate); break; case OpCode::JS_BYTECODE: if (IsStateSplit(gate)) { endStateList_.emplace_back(gate); } break; default: { if (linearizer_->onlyBB_) { auto &info = linearizer_->GetGateInfo(gate); info.region = currentRegion_; linearizer_->BindGate(gate, currentRegion_); } break; } } } } bool IsStateSplit(GateRef gate) { size_t stateOut = 0; auto uses = acc_.Uses(gate); for (auto it = uses.begin(); it != uses.end(); it++) { if (acc_.IsStateIn(it)) { auto op = acc_.GetOpCode(*it); switch (op) { case OpCode::IF_TRUE: case OpCode::IF_FALSE: case OpCode::IF_EXCEPTION: case OpCode::IF_SUCCESS: stateOut++; break; default: break; } } } return stateOut > 1; // 1: depend out } private: GraphLinearizer* linearizer_; ChunkDeque pendingList_; ChunkVector endStateList_; GateAccessor acc_; GateRegion* currentRegion_ {nullptr}; bool scheduleLIR_; }; class ImmediateDominatorsGenerator { public: explicit ImmediateDominatorsGenerator(GraphLinearizer *linearizer, Chunk* chunk, size_t size) : linearizer_(linearizer), pendingList_(chunk), dfsList_(chunk), dfsTimestamp_(size, chunk), dfsFatherIdx_(size, chunk), bbDfsTimestampToIdx_(size, chunk), semiDom_(size, chunk), immDom_(size, chunk), minIdx_(size, chunk), parentIdx_(size, chunk), semiDomTree_(chunk) {} void Run() { BuildDfsFather(); BuildDomTree(); BuildImmediateDominator(); BuildImmediateDominatorDepth(); } void BuildDfsFather() { size_t timestamp = 0; auto entry = linearizer_->regionList_.front(); linearizer_->circuit_->AdvanceTime(); entry->SetVisited(linearizer_->acc_); ASSERT(pendingList_.empty()); pendingList_.emplace_back(entry); while (!pendingList_.empty()) { auto curRegion = pendingList_.back(); pendingList_.pop_back(); dfsList_.emplace_back(curRegion->id_); dfsTimestamp_[curRegion->id_] = timestamp++; for (auto succ : curRegion->succs_) { if (!succ->IsVisited(linearizer_->acc_)) { succ->SetVisited(linearizer_->acc_); pendingList_.emplace_back(succ); dfsFatherIdx_[succ->id_] = dfsTimestamp_[curRegion->id_]; } } } for (size_t idx = 0; idx < dfsList_.size(); idx++) { bbDfsTimestampToIdx_[dfsList_[idx]] = idx; } ASSERT(timestamp == linearizer_->regionList_.size()); } size_t UnionFind(size_t idx) { std::stack allIdxs; allIdxs.emplace(idx); size_t pIdx = parentIdx_[idx]; while (pIdx != allIdxs.top()) { allIdxs.emplace(pIdx); pIdx = parentIdx_[pIdx]; } size_t unionFindSetRoot = pIdx; while (!allIdxs.empty()) { if (semiDom_[minIdx_[allIdxs.top()]] > semiDom_[minIdx_[pIdx]]) { minIdx_[allIdxs.top()] = minIdx_[pIdx]; } parentIdx_[allIdxs.top()] = unionFindSetRoot; pIdx = allIdxs.top(); allIdxs.pop(); } return unionFindSetRoot; } void Merge(size_t fatherIdx, size_t sonIdx) { size_t parentFatherIdx = UnionFind(fatherIdx); size_t parentSonIdx = UnionFind(sonIdx); parentIdx_[parentSonIdx] = parentFatherIdx; } void BuildDomTree() { auto ®ionList = linearizer_->regionList_; for (size_t i = 0; i < dfsList_.size(); i++) { ChunkDeque sonList(linearizer_->chunk_); semiDomTree_.emplace_back(std::move(sonList)); } std::iota(parentIdx_.begin(), parentIdx_.end(), 0); std::iota(semiDom_.begin(), semiDom_.end(), 0); semiDom_[0] = semiDom_.size(); ASSERT(dfsList_.size() > 0); for (size_t idx = dfsList_.size() - 1; idx >= 1; idx--) { for (const auto &preRegion : regionList[dfsList_[idx]]->preds_) { size_t preRegionIdx = bbDfsTimestampToIdx_[preRegion->id_]; if (bbDfsTimestampToIdx_[preRegion->id_] < idx) { semiDom_[idx] = std::min(semiDom_[idx], preRegionIdx); } else { UnionFind(preRegionIdx); semiDom_[idx] = std::min(semiDom_[idx], semiDom_[minIdx_[preRegionIdx]]); } } for (const auto &succDomIdx : semiDomTree_[idx]) { UnionFind(succDomIdx); if (idx == semiDom_[minIdx_[succDomIdx]]) { immDom_[succDomIdx] = idx; } else { immDom_[succDomIdx] = minIdx_[succDomIdx]; } } minIdx_[idx] = idx; Merge(dfsFatherIdx_[dfsList_[idx]], idx); semiDomTree_[semiDom_[idx]].emplace_back(idx); } } void BuildImmediateDominator() { for (size_t idx = 1; idx < dfsList_.size(); idx++) { if (immDom_[idx] != semiDom_[idx]) { immDom_[idx] = immDom_[immDom_[idx]]; } } auto entry = linearizer_->regionList_.front(); entry->iDominator_ = entry; entry->depth_ = 0; auto ®ionList = linearizer_->regionList_; for (size_t i = 1; i < immDom_.size(); i++) { auto index = dfsList_[i]; auto dominatedRegion = regionList[index]; auto domIndex = dfsList_[immDom_[i]]; auto immDomRegion = regionList[domIndex]; immDomRegion->depth_ = static_cast(immDom_[i]); dominatedRegion->iDominator_ = immDomRegion; immDomRegion->dominatedRegions_.emplace_back(dominatedRegion); } } void BuildImmediateDominatorDepth() { auto entry = linearizer_->regionList_.front(); entry->depth_ = 0; ASSERT(pendingList_.empty()); pendingList_.emplace_back(entry); while (!pendingList_.empty()) { auto curRegion = pendingList_.back(); pendingList_.pop_back(); for (auto succ : curRegion->dominatedRegions_) { ASSERT(succ->iDominator_->depth_ != GateRegion::INVALID_DEPTH); succ->depth_ = succ->iDominator_->depth_ + 1; pendingList_.emplace_back(succ); } } } private: GraphLinearizer* linearizer_; ChunkDeque pendingList_; ChunkVector dfsList_; ChunkVector dfsTimestamp_; ChunkVector dfsFatherIdx_; ChunkVector bbDfsTimestampToIdx_; ChunkVector semiDom_; ChunkVector immDom_; ChunkVector minIdx_; ChunkVector parentIdx_; ChunkVector> semiDomTree_; }; class LoopInfoBuilder { public: explicit LoopInfoBuilder(GraphLinearizer *linearizer, Chunk* chunk) : linearizer_(linearizer), pendingList_(chunk), loopbacks_(chunk), chunk_(chunk), dfsStack_(chunk), acc_(linearizer->circuit_) {} void Run() { ComputeLoopNumber(); ComputeLoopInfo(); ComputeLoopTree(); if (linearizer_->IsLogEnabled()) { for (size_t i = 0; i < numLoops_; i++) { auto& loopInfo = linearizer_->loops_[i]; PrintLoop(loopInfo); } } } void PrintLoop(GraphLinearizer::LoopInfo& loopInfo) { auto size = linearizer_->regionList_.size(); LOG_COMPILER(INFO) << "--------------------------------- LoopInfo Start ---------------------------------"; LOG_COMPILER(INFO) << "Head: " << acc_.GetId(loopInfo.loopHead->state_); LOG_COMPILER(INFO) << "loopNumber: " << loopInfo.loopHead->loopNumber_; LOG_COMPILER(INFO) << "Body: ["; for (size_t i = 0; i < size; i++) { if (loopInfo.loopBodys->TestBit(i)) { auto current = linearizer_->regionList_.at(i)->state_; LOG_COMPILER(INFO) << acc_.GetId(current) << ", "; } } LOG_COMPILER(INFO) << "]"; LOG_COMPILER(INFO) << "Exit: ["; if (loopInfo.loopExits != nullptr) { for (auto region : *loopInfo.loopExits) { auto current = region->state_; LOG_COMPILER(INFO) << acc_.GetId(current) << ", "; } } LOG_COMPILER(INFO) << "]"; LOG_COMPILER(INFO) << "--------------------------------- LoopInfo End ---------------------------------"; } void ComputeLoopInfo() { linearizer_->loops_.clear(); auto size = linearizer_->regionList_.size(); linearizer_->loops_.resize(numLoops_, GraphLinearizer::LoopInfo()); for (auto curState : loopbacks_) { GateRegion* curRegion = curState.region; GateRegion* loopHead = curRegion->succs_[curState.index]; auto loopNumber = loopHead->GetLoopNumber(); auto& loopInfo = linearizer_->loops_[loopNumber]; if (loopInfo.loopHead == nullptr) { loopInfo.loopHead = loopHead; loopInfo.loopBodys = chunk_->New(chunk_, size); loopInfo.loopIndex = static_cast(loopNumber); loopInfo.loopHead->loopIndex_ = static_cast(loopInfo.loopIndex); } if (curRegion != loopHead) { loopInfo.loopBodys->SetBit(curRegion->GetId()); pendingList_.emplace_back(curRegion); } PropagateLoopBody(loopInfo); } } void PropagateLoopBody(GraphLinearizer::LoopInfo& loopInfo) { while (!pendingList_.empty()) { auto curRegion = pendingList_.back(); pendingList_.pop_back(); for (auto pred : curRegion->preds_) { if (pred != loopInfo.loopHead) { if (!loopInfo.loopBodys->TestBit(pred->GetId())) { loopInfo.loopBodys->SetBit(pred->GetId()); pendingList_.emplace_back(pred); } } } } } void ComputeLoopNumber() { auto size = linearizer_->regionList_.size(); dfsStack_.resize(size, DFSState(nullptr, 0)); linearizer_->circuit_->AdvanceTime(); auto entry = linearizer_->regionList_.front(); auto currentDepth = Push(entry, 0); while (currentDepth > 0) { auto& curState = dfsStack_[currentDepth - 1]; // -1: for current auto curRegion = curState.region; auto index = curState.index; if (index == curRegion->succs_.size()) { currentDepth--; curRegion->SetFinished(acc_); } else { GateRegion* succ = curRegion->succs_[index]; curState.index = ++index; if (succ->IsFinished(acc_)) { continue; } if (succ->IsUnvisited(acc_)) { currentDepth = Push(succ, currentDepth); } else { ASSERT(succ->IsVisited(acc_) && index > 0); loopbacks_.emplace_back(DFSState(curRegion, index - 1)); // -1: for prev if (!succ->HasLoopNumber()) { succ->SetLoopNumber(numLoops_++); } } } } } void ComputeLoopTree() { linearizer_->circuit_->AdvanceTime(); auto entry = linearizer_->regionList_.front(); GraphLinearizer::LoopInfo *loopInfo = nullptr; auto currentDepth = Push(entry, 0); while (currentDepth > 0) { auto &curState = dfsStack_[currentDepth - 1]; // -1: for current auto curRegion = curState.region; auto index = curState.index; GateRegion* succ = nullptr; if (index >= curRegion->succs_.size()) { if (curRegion->HasLoopNumber()) { if (curRegion->IsVisited(acc_)) { ASSERT(loopInfo != nullptr && loopInfo->loopHead == curRegion); loopInfo = loopInfo->outer; curRegion->SetFinished(acc_); } auto loopExitIndex = index - curRegion->succs_.size(); auto& currentInfo = linearizer_->loops_[curRegion->GetLoopNumber()]; if (currentInfo.loopExits != nullptr && loopExitIndex < currentInfo.loopExits->size()) { succ = currentInfo.loopExits->at(loopExitIndex); curState.index++; } } } else { succ = curRegion->succs_[curState.index++]; // goto next } if (succ != nullptr) { if (!succ->IsUnvisited(acc_)) { continue; } if (loopInfo != nullptr && !loopInfo->loopBodys->TestBit(succ->GetId())) { AddLoopExit(succ, loopInfo); } else if (succ->IsUnvisited(acc_)) { currentDepth = Push(succ, currentDepth); loopInfo = EnterInnerLoop(succ, loopInfo); } } else { if (!curRegion->HasLoopNumber()) { curRegion->SetFinished(acc_); } currentDepth--; } } } void AddLoopExit(GateRegion* succ, GraphLinearizer::LoopInfo *loopInfo) { if (loopInfo->loopExits == nullptr) { loopInfo->loopExits = chunk_->New>(chunk_); } loopInfo->loopExits->emplace_back(succ); } GraphLinearizer::LoopInfo *EnterInnerLoop(GateRegion* succ, GraphLinearizer::LoopInfo *loopInfo) { // enter inner loop if (succ->HasLoopNumber()) { ASSERT_PRINT(succ->loopIndex_ != GateRegion::INVALID_INDEX, "GateRegion's index should be assigned"); auto& innerLoop = linearizer_->loops_[succ->GetLoopNumber()]; innerLoop.outer = loopInfo; if (loopInfo != nullptr) { innerLoop.loopDepth = loopInfo->loopDepth + 1; } else { innerLoop.loopDepth = 1; } succ->loopDepth_ = static_cast(innerLoop.loopDepth); loopInfo = &innerLoop; } else if (loopInfo != nullptr) { succ->loopIndex_ = static_cast(loopInfo->loopIndex); succ->loopDepth_ = static_cast(loopInfo->loopDepth); } return loopInfo; } size_t Push(GateRegion *region, size_t depth) { if (region->IsUnvisited(acc_)) { dfsStack_[depth].region = region; dfsStack_[depth].index = 0; region->SetVisited(acc_); return depth + 1; } return depth; } private: struct DFSState { DFSState(GateRegion *region, size_t index) : region(region), index(index) {} GateRegion *region; size_t index; }; GraphLinearizer* linearizer_ {nullptr}; ChunkDeque pendingList_; ChunkVector loopbacks_; Chunk* chunk_ {nullptr}; ChunkVector dfsStack_; GateAccessor acc_; size_t numLoops_{0}; }; class GateScheduler { public: explicit GateScheduler(GraphLinearizer *linearizer) : linearizer_(linearizer), fixedGateList_(linearizer->chunk_), pendingList_(linearizer->chunk_), acc_(linearizer->circuit_), scheduleUpperBound_(linearizer_->scheduleUpperBound_) {} void InitializeFixedGate() { auto ®ionRoots = linearizer_->regionRootList_; auto size = regionRoots.size(); for (size_t i = 0; i < size; i++) { auto fixedGate = regionRoots[i]; auto region = linearizer_->GateToRegion(fixedGate); // fixed Gate's output auto uses = acc_.Uses(fixedGate); for (auto it = uses.begin(); it != uses.end(); it++) { GateRef succGate = *it; if (acc_.IsStateIn(it) && acc_.IsSelector(succGate)) { linearizer_->AddFixedGateToRegion(succGate, region); fixedGateList_.emplace_back(succGate); } } } } void Prepare() { InitializeFixedGate(); auto ®ionRoots = linearizer_->regionRootList_; ASSERT(pendingList_.empty()); for (const auto rootGate : regionRoots) { pendingList_.emplace_back(rootGate); } while (!pendingList_.empty()) { auto curGate = pendingList_.back(); pendingList_.pop_back(); auto numIns = acc_.GetNumIns(curGate); for (size_t i = 0; i < numIns; i++) { VisitPreparedGate(Edge(curGate, i)); } } } void ScheduleUpperBound() { if (!scheduleUpperBound_) { return; } auto ®ionRoots = linearizer_->regionRootList_; ASSERT(pendingList_.empty()); for (const auto rootGate : regionRoots) { pendingList_.emplace_back(rootGate); } while (!pendingList_.empty()) { auto curGate = pendingList_.back(); pendingList_.pop_back(); auto uses = acc_.Uses(curGate); for (auto useIt = uses.begin(); useIt != uses.end(); useIt++) { VisitUpperBoundGate(useIt.GetEdge()); } } } void VisitUpperBoundGate(Edge edge) { GateRef succGate = edge.GetGate(); auto& succInfo = linearizer_->GetGateInfo(succGate); if (!succInfo.IsSchedulable()) { return; } ASSERT(succInfo.upperBound != nullptr); auto curGate = acc_.GetIn(succGate, edge.GetIndex()); auto curUpperBound = linearizer_->GateToUpperBound(curGate); ASSERT(IsInSameDominatorChain(curUpperBound, succInfo.upperBound)); if (curUpperBound->depth_ > succInfo.upperBound->depth_) { succInfo.upperBound = curUpperBound; pendingList_.emplace_back(succGate); } } void ScheduleFloatingGate() { auto ®ionRoots = linearizer_->regionRootList_; for (const auto rootGate : regionRoots) { auto ins = acc_.Ins(rootGate); for (auto it = ins.begin(); it != ins.end(); it++) { pendingList_.emplace_back(*it); while (!pendingList_.empty()) { auto curGate = pendingList_.back(); pendingList_.pop_back(); ComputeLowerBoundAndScheduleGate(curGate); } } } } void VisitPreparedGate(Edge edge) { auto curGate = edge.GetGate(); auto prevGate = acc_.GetIn(curGate, edge.GetIndex()); if (acc_.IsProlog(prevGate) || acc_.IsRoot(prevGate)) { return; } auto& prevInfo = linearizer_->GetGateInfo(prevGate); if (prevInfo.IsNone()) { if (scheduleUpperBound_) { prevInfo.upperBound = linearizer_->GetEntryRegion(); } ASSERT(prevInfo.schedulableUseCount == 0); prevInfo.state_ = GraphLinearizer::ScheduleState::SCHEDELABLE; pendingList_.emplace_back(prevGate); } auto& curInfo = linearizer_->GetGateInfo(curGate); if (prevInfo.IsSchedulable() && curInfo.IsSchedulable()) { prevInfo.schedulableUseCount++; } } void ComputeLowerBoundAndScheduleGate(GateRef curGate) { auto& curInfo = linearizer_->GetGateInfo(curGate); if (!curInfo.IsSchedulable() || (curInfo.schedulableUseCount != 0) || (curInfo.region != nullptr)) { return; } auto region = GetCommonDominatorOfAllUses(curGate); if (scheduleUpperBound_) { ASSERT(curInfo.upperBound != nullptr); ASSERT(linearizer_->GetCommonDominator(region, curInfo.upperBound) == curInfo.upperBound); auto uppermost = curInfo.upperBound->depth_; auto upperRegion = GetUpperBoundRegion(region); while (upperRegion != nullptr && upperRegion->depth_ >= uppermost) { region = upperRegion; upperRegion = GetUpperBoundRegion(region); } } if (acc_.GetOpCode(curGate) == OpCode::FINISH_ALLOCATE) { ScheduleAllocRegion(curGate, region); } else { ScheduleGate(curGate, region); } } GateRegion* GetUpperBoundRegion(GateRegion* region) { if (region->IsLoopHead()) { return region->iDominator_; } auto loopInfo = linearizer_->GetLoopInfo(region); if (loopInfo != nullptr) { if (!CheckRegionDomLoopExist(region, loopInfo)) { return nullptr; } return loopInfo->loopHead->iDominator_; } return nullptr; } void ScheduleAllocRegion(GateRef gate, GateRegion* region) { ASSERT(acc_.GetOpCode(gate) == OpCode::FINISH_ALLOCATE); // 1. schedule FINISH_ALLOCATE first ScheduleGate(gate, region); GateRef curGate = acc_.GetDep(gate); [[maybe_unused]]GateRef output = acc_.GetValueIn(gate, 0); // 2. schedule all gates from end to start while (acc_.GetOpCode(curGate) != OpCode::START_ALLOCATE) { [[maybe_unused]] auto& curInfo = linearizer_->GetGateInfo(curGate); ASSERT(curInfo.IsSchedulable()); ASSERT(curInfo.schedulableUseCount == 0); ASSERT(curInfo.region == nullptr); ASSERT(acc_.GetStateCount(curGate) == 0); ASSERT(acc_.GetDependCount(curGate) == 1); ASSERT(acc_.GetValueUsesCount(curGate) == 0 || curGate == output); ScheduleGate(curGate, region); curGate = acc_.GetDep(curGate); } // 3. schedule START_ALLOCATE ASSERT(linearizer_->GetGateInfo(curGate).schedulableUseCount == 0); ScheduleGate(curGate, region); } bool CheckRegionDomLoopExist(GateRegion* region, GraphLinearizer::LoopInfo* loopInfo) { if (loopInfo->loopExits == nullptr) { return true; } for (auto exitRegion : *loopInfo->loopExits) { if (linearizer_->GetCommonDominator(region, exitRegion) != region) { return false; } } return true; } void ScheduleGate(GateRef gate, GateRegion* region) { auto ins = acc_.Ins(gate); for (auto it = ins.begin(); it != ins.end(); it++) { auto inputGate = *it; auto& inputInfo = linearizer_->GetGateInfo(inputGate); if (!inputInfo.IsSchedulable()) { continue; } inputInfo.schedulableUseCount--; if (inputInfo.schedulableUseCount == 0) { pendingList_.emplace_back(inputGate); } } ASSERT(!linearizer_->IsScheduled(gate)); linearizer_->BindGate(gate, region); } GateRegion* GetCommonDominatorOfAllUses(GateRef curGate) { GateRegion* region = nullptr; auto uses = acc_.Uses(curGate); for (auto useIt = uses.begin(); useIt != uses.end(); useIt++) { GateRef useGate = *useIt; auto& useInfo = linearizer_->GetGateInfo(useGate); if (useInfo.IsNone()) { continue; } GateRegion* useRegion = useInfo.region; if (useInfo.IsSelector()) { GateRef state = acc_.GetState(useGate); ASSERT(acc_.IsCFGMerge(state) && useIt.GetIndex() > 0); useGate = acc_.GetIn(state, useIt.GetIndex() - 1); // -1: for state useRegion = linearizer_->FindPredRegion(useGate); } else if (acc_.IsCFGMerge(useGate)) { useGate = acc_.GetIn(useGate, useIt.GetIndex()); useRegion = linearizer_->FindPredRegion(useGate); } if (region == nullptr) { region = useRegion; } else { ASSERT(useRegion != nullptr); region = linearizer_->GetCommonDominator(region, useRegion); } } return region; } bool IsInSameDominatorChain(GateRegion* left, GateRegion* right) const { auto dom = linearizer_->GetCommonDominator(left, right); return left == dom || right == dom; } void ScheduleFixedGate() { for (auto gate : fixedGateList_) { GateRegion* region = linearizer_->GateToRegion(gate); linearizer_->BindGate(gate, region); } #ifndef NDEBUG Verify(); #endif } void Verify() { std::vector gateList; linearizer_->circuit_->GetAllGates(gateList); for (const auto &gate : gateList) { auto& gateInfo = linearizer_->GetGateInfo(gate); if (gateInfo.IsSchedulable()) { ASSERT(linearizer_->IsScheduled(gate)); } ASSERT(gateInfo.schedulableUseCount == 0); } } private: GraphLinearizer* linearizer_ {nullptr}; ChunkVector fixedGateList_; ChunkDeque pendingList_; GateAccessor acc_; bool scheduleUpperBound_{false}; }; void GraphLinearizer::LinearizeGraph() { gateIdToGateInfo_.resize(circuit_->GetMaxGateId() + 1, GateInfo{nullptr}); // 1: max + 1 = size CFGBuilder builder(this); builder.Run(); ImmediateDominatorsGenerator generator(this, chunk_, regionList_.size()); generator.Run(); if (IsEnableLoopInvariantCodeMotion() && loopNumber_ > 0) { scheduleUpperBound_ = true; LoopInfoBuilder loopInfoBuilder(this, chunk_); loopInfoBuilder.Run(); } if (!onlyBB_) { GateScheduler scheduler(this); scheduler.Prepare(); scheduler.ScheduleUpperBound(); scheduler.ScheduleFloatingGate(); scheduler.ScheduleFixedGate(); } } void GraphLinearizer::CreateGateRegion(GateRef gate) { ASSERT(GateToRegion(gate) == nullptr); auto region = new (chunk_) GateRegion(chunk_); region->id_ = regionList_.size(); regionList_.emplace_back(region); if (acc_.GetOpCode(gate) == OpCode::LOOP_BEGIN) { loopNumber_++; region->stateKind_ = GateRegion::StateKind::LOOP_HEAD; } AddRootGateToRegion(gate, region); } void GraphLinearizer::LinearizeRegions(ControlFlowGraph &result) { size_t liveNum = OptimizeCFG(); ASSERT(result.size() == 0); result.resize(liveNum); auto uses = acc_.Uses(acc_.GetArgRoot()); for (auto useIt = uses.begin(); useIt != uses.end(); useIt++) { regionList_.front()->gateList_.emplace_back(*useIt); } size_t i = 0; for (size_t id = 0; id < regionList_.size(); id++) { GateRegion* r = regionList_[id]; if (r->IsDead()) { continue; } auto& gates = r->GetGates(); auto& bb = result[i]; bb.insert(bb.end(), gates.begin(), gates.end()); i++; } } bool GateRegion::IsSimple(GateAccessor *acc) const { for (auto g : gateList_) { bool isSimple = acc->IsSimpleState(g); bool complexOut = HasComplexOuts(); if (!isSimple || complexOut) { return false; } } return true; } size_t GraphLinearizer::OptimizeControls(GateRegion *region) { size_t deads = 0; GateRegion* target = region; do { GateRegion* succ = target->GetSimpleSuccRegion(); if (succ == nullptr) { break; } MoveAndClear(target, succ); target = succ; deads++; } while (target->IsSimple(&acc_)); return deads; } void GraphLinearizer::MoveAndClear(GateRegion* from, GateRegion* to) { ASSERT(from != to); ASSERT(to->GetPreds().size() == 1); for (GateRef g: from->GetGates()) { ASSERT(acc_.IsSimpleState(g)); OpCode op = acc_.GetOpCode(g); switch (op) { case OpCode::IF_TRUE: case OpCode::IF_FALSE: case OpCode::SWITCH_CASE: case OpCode::DEFAULT_CASE: case OpCode::LOOP_BACK: case OpCode::ORDINARY_BLOCK: case OpCode::MERGE: case OpCode::VALUE_SELECTOR: to->AddGate(g); break; default: break; } } for (auto p : from->GetPreds()) { p->ReplaceSucc(from, to); } to->RemovePred(from); from->SetDead(); #ifndef NDEBUG from->Clear(); #endif } size_t GraphLinearizer::OptimizeCFG() { size_t liveNum = regionList_.size(); for (size_t i = 0; i < regionList_.size(); i++) { GateRegion* src = regionList_[i]; if (!src->IsDead() && src->IsSimple(&acc_)) { size_t dead = OptimizeControls(src); ASSERT(liveNum >= dead); liveNum -= dead; } } return liveNum; } GateRegion* GraphLinearizer::FindPredRegion(GateRef input) { GateRegion* predRegion = GateToRegion(input); while (predRegion == nullptr) { ASSERT(acc_.GetStateCount(input) == 1); // 1: fall through block input = acc_.GetState(input); predRegion = GateToRegion(input); } ASSERT(predRegion != nullptr); return predRegion; } GateRegion* GraphLinearizer::GetCommonDominator(GateRegion* left, GateRegion* right) const { while (left != right) { if (left->depth_ < right->depth_) { right = right->iDominator_; } else { left = left->iDominator_; } } return left; } void GraphLinearizer::PrintGraph(const char* title) { LOG_COMPILER(INFO) << "======================== " << title << " ========================"; int bbIdx = 0; for (size_t i = 0; i < regionList_.size(); i++) { auto bb = regionList_[i]; if (bb->IsDead()) { continue; } auto front = bb->gateList_.front(); auto opcode = acc_.GetOpCode(front); size_t loopHeadId = 0; auto loopInfo = GetLoopInfo(bb); if (loopInfo != nullptr) { loopHeadId = loopInfo->loopHead->id_; } LOG_COMPILER(INFO) << "B" << bb->id_ << "_LB" << bbIdx << ": " << "depth: [" << bb->depth_ << "] " << opcode << "(" << acc_.GetId(front) << ") " << "IDom B" << bb->iDominator_->id_ << " loop Header: " << loopHeadId; std::string log("\tPreds: "); for (size_t k = 0; k < bb->preds_.size(); ++k) { log += std::to_string(bb->preds_[k]->id_) + ", "; } LOG_COMPILER(INFO) << log; std::string log1("\tSucces: "); for (size_t j = 0; j < bb->succs_.size(); j++) { log1 += std::to_string(bb->succs_[j]->id_) + ", "; } LOG_COMPILER(INFO) << log1; for (auto it = bb->gateList_.crbegin(); it != bb->gateList_.crend(); it++) { acc_.Print(*it); } LOG_COMPILER(INFO) << ""; bbIdx++; } } } // namespace panda::ecmascript::kungfu