• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/compiler/bytecode_circuit_builder.h"
17 
18 #include "ecmascript/deoptimizer/deoptimizer.h"
19 #include "ecmascript/interpreter/interpreter-inl.h"
20 
21 
22 namespace panda::ecmascript::kungfu {
BytecodeToCircuit()23 void BytecodeCircuitBuilder::BytecodeToCircuit()
24 {
25     ExceptionInfo exceptionInfo = {};
26 
27     // collect try catch block info
28     CollectTryCatchBlockInfo(exceptionInfo);
29     hasTryCatch_ = exceptionInfo.size() != 0;
30     BuildRegionInfo();
31     // Building the basic block diagram of bytecode
32     BuildRegions(exceptionInfo);
33 }
34 
BuildRegionInfo()35 void BytecodeCircuitBuilder::BuildRegionInfo()
36 {
37     uint32_t size = pcOffsets_.size();
38     ASSERT(size > 0);
39     uint32_t end = size - 1;  // 1: end
40     BytecodeIterator iterator(this, 0, end);
41 
42     infoData_.resize(size);
43     byteCodeToJSGates_.resize(size, std::vector<GateRef>(0));
44     regionsInfo_.InsertHead(0); // 0: start pc
45     iterator.GotoStart();
46     while (!iterator.Done()) {
47         auto index = iterator.Index();
48         auto &info = infoData_[index];
49         auto pc = pcOffsets_[index];
50         info.metaData_ = bytecodes_->GetBytecodeMetaData(pc);
51         ASSERT(!info.metaData_.IsInvalid());
52         BytecodeInfo::InitBytecodeInfo(this, info, pc);
53         CollectRegionInfo(index);
54         ++iterator;
55     }
56 }
57 
CollectRegionInfo(uint32_t bcIndex)58 void BytecodeCircuitBuilder::CollectRegionInfo(uint32_t bcIndex)
59 {
60     auto pc = pcOffsets_[bcIndex];
61     auto &info = infoData_[bcIndex];
62     int32_t offset = 0;
63     if (info.IsJump()) {
64         switch (info.GetOpcode()) {
65             case EcmaOpcode::JEQZ_IMM8:
66             case EcmaOpcode::JNEZ_IMM8:
67             case EcmaOpcode::JMP_IMM8:
68                 offset = static_cast<int8_t>(READ_INST_8_0());
69                 break;
70             case EcmaOpcode::JNEZ_IMM16:
71             case EcmaOpcode::JEQZ_IMM16:
72             case EcmaOpcode::JMP_IMM16:
73                 offset = static_cast<int16_t>(READ_INST_16_0());
74                 break;
75             case EcmaOpcode::JMP_IMM32:
76             case EcmaOpcode::JNEZ_IMM32:
77             case EcmaOpcode::JEQZ_IMM32:
78                 offset = static_cast<int32_t>(READ_INST_32_0());
79                 break;
80             default:
81                 LOG_ECMA(FATAL) << "this branch is unreachable";
82                 UNREACHABLE();
83                 break;
84         }
85         auto nextIndex = bcIndex + 1; // 1: next pc
86         auto targetIndex = FindBcIndexByPc(pc + offset);
87         // condition branch current basic block end
88         if (info.IsCondJump()) {
89             regionsInfo_.InsertSplit(nextIndex);
90             regionsInfo_.InsertJump(targetIndex, bcIndex, false);
91         } else {
92             if (bcIndex != GetLastBcIndex()) {
93                 regionsInfo_.InsertHead(nextIndex);
94             }
95             regionsInfo_.InsertJump(targetIndex, bcIndex, true);
96         }
97     } else if (info.IsReturn() || info.IsThrow()) {
98         if (bcIndex != GetLastBcIndex()) {
99             auto nextIndex = bcIndex + 1; // 1: next pc
100             regionsInfo_.InsertHead(nextIndex);
101         }
102     } else if (info.IsInsufficientProfile()) {
103         if (bcIndex != GetLastBcIndex()) {
104             auto nextIndex = bcIndex + 1; // 1: next pc
105             regionsInfo_.InsertSplit(nextIndex);
106         }
107     }
108 }
109 
CollectTryCatchBlockInfo(ExceptionInfo & byteCodeException)110 void BytecodeCircuitBuilder::CollectTryCatchBlockInfo(ExceptionInfo &byteCodeException)
111 {
112     auto pf = file_->GetPandaFile();
113     panda_file::MethodDataAccessor mda(*pf, method_->GetMethodId());
114     panda_file::CodeDataAccessor cda(*pf, mda.GetCodeId().value());
115 
116     cda.EnumerateTryBlocks([this, &byteCodeException](
117         panda_file::CodeDataAccessor::TryBlock &tryBlock) {
118         auto tryStartOffset = tryBlock.GetStartPc();
119         auto tryEndOffset = tryBlock.GetStartPc() + tryBlock.GetLength();
120 
121         auto tryStartPc = const_cast<uint8_t *>(method_->GetBytecodeArray() + tryStartOffset);
122         auto tryEndPc = const_cast<uint8_t *>(method_->GetBytecodeArray() + tryEndOffset);
123         // skip try blocks with same pc in start and end label
124         if (tryStartPc == tryEndPc) {
125             return true;
126         }
127 
128         auto tryStartBcIndex = FindBcIndexByPc(tryStartPc);
129         regionsInfo_.InsertSplit(tryStartBcIndex);
130         if (tryEndPc <= GetLastPC()) {
131             auto tryEndBcIndex = FindBcIndexByPc(tryEndPc);
132             regionsInfo_.InsertSplit(tryEndBcIndex);
133         }
134         byteCodeException.emplace_back(ExceptionItem { tryStartPc, tryEndPc, {} });
135         tryBlock.EnumerateCatchBlocks([&](panda_file::CodeDataAccessor::CatchBlock &catchBlock) {
136             auto pcOffset = catchBlock.GetHandlerPc();
137             auto catchBlockPc = const_cast<uint8_t *>(method_->GetBytecodeArray() + pcOffset);
138             auto catchBlockBcIndex = FindBcIndexByPc(catchBlockPc);
139             regionsInfo_.InsertHead(catchBlockBcIndex);
140             // try block associate catch block
141             byteCodeException.back().catches.emplace_back(catchBlockPc);
142             return true;
143         });
144         return true;
145     });
146 }
147 
IsAncestor(size_t nodeA,size_t nodeB)148 bool BytecodeCircuitBuilder::IsAncestor(size_t nodeA, size_t nodeB)
149 {
150     return timeIn_[bbIdToDfsTimestamp_[nodeA]] <= timeIn_[bbIdToDfsTimestamp_[nodeB]] &&
151         timeOut_[bbIdToDfsTimestamp_[nodeA]] >= timeOut_[bbIdToDfsTimestamp_[nodeB]];
152 }
153 
PerformDFS(const std::vector<size_t> & immDom,size_t listSize)154 void BytecodeCircuitBuilder::PerformDFS(const std::vector<size_t> &immDom, size_t listSize)
155 {
156     std::vector<std::vector<size_t>> sonList(listSize);
157     for (size_t idx = 1; idx < immDom.size(); idx++) {
158         sonList[immDom[idx]].push_back(idx);
159     }
160 
161     {
162         size_t timestamp = 0;
163         struct DFSState {
164             size_t cur;
165             std::vector<size_t> &succList;
166             size_t idx;
167         };
168         std::stack<DFSState> dfsStack;
169         size_t root = 0;
170         dfsStack.push({root, sonList[root], 0});
171         timeIn_[root] = timestamp++;
172         while (!dfsStack.empty()) {
173             auto &curState = dfsStack.top();
174             auto &cur = curState.cur;
175             auto &succList = curState.succList;
176             auto &idx = curState.idx;
177             if (idx == succList.size()) {
178                 timeOut_[cur] = timestamp++;
179                 dfsStack.pop();
180                 continue;
181             }
182             const auto &succ = succList[idx];
183             dfsStack.push({succ, sonList[succ], 0});
184             timeIn_[succ] = timestamp++;
185             idx++;
186         }
187     }
188 }
189 
ReducibilityCheck()190 void BytecodeCircuitBuilder::ReducibilityCheck()
191 {
192     std::vector<size_t> basicBlockList;
193     std::vector<size_t> immDom;
194     std::unordered_map<size_t, size_t> bbDfsTimestampToIdx;
195     ComputeDominatorTree(basicBlockList, immDom, bbDfsTimestampToIdx);
196     timeIn_.resize(basicBlockList.size());
197     timeOut_.resize(basicBlockList.size());
198     PerformDFS(immDom, basicBlockList.size());
199 }
200 
ComputeImmediateDominators(const std::vector<size_t> & basicBlockList,std::unordered_map<size_t,size_t> & dfsFatherIdx,std::vector<size_t> & immDom,std::unordered_map<size_t,size_t> & bbDfsTimestampToIdx)201 void BytecodeCircuitBuilder::ComputeImmediateDominators(const std::vector<size_t> &basicBlockList,
202                                                         std::unordered_map<size_t, size_t> &dfsFatherIdx,
203                                                         std::vector<size_t> &immDom,
204                                                         std::unordered_map<size_t, size_t> &bbDfsTimestampToIdx)
205 {
206     std::vector<size_t> semiDom(basicBlockList.size());
207     std::vector<std::vector<size_t> > semiDomTree(basicBlockList.size());
208     {
209         std::vector<size_t> parent(basicBlockList.size());
210         std::iota(parent.begin(), parent.end(), 0);
211         std::vector<size_t> minIdx(basicBlockList.size());
212         std::function<size_t(size_t)> unionFind = [&] (size_t idx) -> size_t {
213             if (parent[idx] == idx) return idx;
214             size_t unionFindSetRoot = unionFind(parent[idx]);
215             if (semiDom[minIdx[idx]] > semiDom[minIdx[parent[idx]]]) {
216                 minIdx[idx] = minIdx[parent[idx]];
217             }
218             return parent[idx] = unionFindSetRoot;
219         };
220         auto merge = [&] (size_t fatherIdx, size_t sonIdx) -> void {
221             size_t parentFatherIdx = unionFind(fatherIdx);
222             size_t parentSonIdx = unionFind(sonIdx);
223             parent[parentSonIdx] = parentFatherIdx;
224         };
225         auto calculateSemiDom = [&](size_t idx, const ChunkVector<BytecodeRegion *> &blocks) {
226             for (const auto &preBlock : blocks) {
227                 if (bbDfsTimestampToIdx[preBlock->id] < idx) {
228                     semiDom[idx] = std::min(semiDom[idx], bbDfsTimestampToIdx[preBlock->id]);
229                 } else {
230                     unionFind(bbDfsTimestampToIdx[preBlock->id]);
231                     semiDom[idx] = std::min(semiDom[idx], semiDom[minIdx[bbDfsTimestampToIdx[preBlock->id]]]);
232                 }
233             }
234         };
235         std::iota(semiDom.begin(), semiDom.end(), 0);
236         semiDom[0] = semiDom.size();
237         for (size_t idx = basicBlockList.size() - 1; idx >= 1; idx--) {
238             calculateSemiDom(idx, graph_[basicBlockList[idx]]->preds);
239             if (!graph_[basicBlockList[idx]]->trys.empty()) {
240                 calculateSemiDom(idx, graph_[basicBlockList[idx]]->trys);
241             }
242             for (const auto &succDomIdx : semiDomTree[idx]) {
243                 unionFind(succDomIdx);
244                 if (idx == semiDom[minIdx[succDomIdx]]) {
245                     immDom[succDomIdx] = idx;
246                 } else {
247                     immDom[succDomIdx] = minIdx[succDomIdx];
248                 }
249             }
250             minIdx[idx] = idx;
251             merge(dfsFatherIdx[basicBlockList[idx]], idx);
252             semiDomTree[semiDom[idx]].emplace_back(idx);
253         }
254         for (size_t idx = 1; idx < basicBlockList.size(); idx++) {
255             if (immDom[idx] != semiDom[idx]) {
256                 immDom[idx] = immDom[immDom[idx]];
257             }
258         }
259         semiDom[0] = 0;
260     }
261 }
262 
ComputeDominatorTree(std::vector<size_t> & basicBlockList,std::vector<size_t> & immDom,std::unordered_map<size_t,size_t> & bbDfsTimestampToIdx)263 void BytecodeCircuitBuilder::ComputeDominatorTree(std::vector<size_t> &basicBlockList, std::vector<size_t> &immDom,
264     std::unordered_map<size_t, size_t> &bbDfsTimestampToIdx)
265 {
266     std::unordered_map<size_t, size_t> dfsFatherIdx;
267     size_t timestamp = 0;
268     std::deque<size_t> pendingList;
269     std::vector<size_t> visited(graph_.size(), 0);
270     auto basicBlockId = graph_[0]->id;
271     visited[graph_[0]->id] = 1;
272     pendingList.emplace_back(basicBlockId);
273 
274     auto visitConnectedBlocks = [&](const ChunkVector<BytecodeRegion *> &succs, size_t curBlockId) {
275         for (const auto &succBlock : succs) {
276             if (visited[succBlock->id] == 0) {
277                 visited[succBlock->id] = 1;
278                 pendingList.emplace_back(succBlock->id);
279                 dfsFatherIdx[succBlock->id] = bbIdToDfsTimestamp_[curBlockId];
280             }
281         }
282     };
283 
284     while (!pendingList.empty()) {
285         size_t curBlockId = pendingList.back();
286         pendingList.pop_back();
287         basicBlockList.emplace_back(curBlockId);
288         bbIdToDfsTimestamp_[curBlockId] = timestamp++;
289         visitConnectedBlocks(graph_[curBlockId]->succs, curBlockId);
290         if (!graph_[curBlockId]->catches.empty()) {
291             visitConnectedBlocks(graph_[curBlockId]->catches, curBlockId);
292         }
293     }
294     for (size_t idx = 0; idx < basicBlockList.size(); idx++) {
295         bbDfsTimestampToIdx[basicBlockList[idx]] = idx;
296     }
297 
298     immDom.resize(basicBlockList.size());
299     ComputeImmediateDominators(basicBlockList, dfsFatherIdx, immDom, bbDfsTimestampToIdx);
300 }
301 
BuildEntryBlock()302 void BytecodeCircuitBuilder::BuildEntryBlock()
303 {
304     BytecodeRegion &entryBlock = RegionAt(0);
305     BytecodeRegion &nextBlock = RegionAt(1);
306     entryBlock.succs.emplace_back(&nextBlock);
307     nextBlock.preds.emplace_back(&entryBlock);
308     entryBlock.bytecodeIterator_.Reset(this, 0, BytecodeIterator::INVALID_INDEX);
309 }
310 
BuildBasicBlock()311 void BytecodeCircuitBuilder::BuildBasicBlock()
312 {
313     auto &items = regionsInfo_.GetBlockItems();
314     size_t blockId = 1;
315     for (const auto &item : items) {
316         auto &curBlock = GetBasicBlockById(blockId);
317         curBlock.id = blockId;
318         curBlock.start = item.GetStartBcIndex();
319         if (blockId != 1) {
320             auto &prevBlock = RegionAt(blockId - 1);
321             ASSERT(curBlock.start >= 1);
322             prevBlock.end = curBlock.start - 1;
323             prevBlock.bytecodeIterator_.Reset(this, prevBlock.start, prevBlock.end);
324             // fall through
325             if (!item.IsHeadBlock()) {
326                 curBlock.preds.emplace_back(&prevBlock);
327                 prevBlock.succs.emplace_back(&curBlock);
328             }
329         }
330         blockId++;
331     }
332     auto &lastBlock = RegionAt(blockId - 1); // 1: last block
333     lastBlock.end = GetLastBcIndex();
334     lastBlock.bytecodeIterator_.Reset(this, lastBlock.start, lastBlock.end);
335 }
336 
BuildRegions(const ExceptionInfo & byteCodeException)337 void BytecodeCircuitBuilder::BuildRegions(const ExceptionInfo &byteCodeException)
338 {
339     auto blockSize = regionsInfo_.GetBlockItems().size();
340 
341     // 1 : entry block. if the loop head is in the first bb block, the variables used in the head cannot correctly
342     // generate Phi nodes through the dominator-tree algorithm, resulting in an infinite loop. Therefore, an empty
343     // BB block is generated as an entry block
344     graph_.resize(blockSize + 1, nullptr);
345     for (size_t i = 0; i < graph_.size(); i++) {
346         graph_[i] = circuit_->chunk()->New<BytecodeRegion>(circuit_->chunk());
347     }
348 
349     // build entry block
350     BuildEntryBlock();
351 
352     // build basic block
353     BuildBasicBlock();
354 
355     auto &splitItems = regionsInfo_.GetSplitItems();
356     for (const auto &item : splitItems) {
357         auto curIndex = regionsInfo_.FindBBIndexByBcIndex(item.startBcIndex);
358         auto &curBlock = GetBasicBlockById(curIndex);
359         auto predIndex = regionsInfo_.FindBBIndexByBcIndex(item.predBcIndex);
360         auto &predBlock = GetBasicBlockById(predIndex);
361         curBlock.preds.emplace_back(&predBlock);
362         predBlock.succs.emplace_back(&curBlock);
363     }
364 
365     if (byteCodeException.size() != 0) {
366         BuildCatchBlocks(byteCodeException);
367     }
368     UpdateCFG();
369     if (HasTryCatch()) {
370         CollectTryPredsInfo();
371     }
372     RemoveUnreachableRegion();
373     if (IsLogEnabled() && !IsPreAnalysis()) {
374         PrintGraph(std::string("Update CFG [" + methodName_ + "]").c_str());
375     }
376     frameStateBuilder_.AnalyzeLiveness();
377     RemoveInsufficientProfileRegion();
378     if (NeedIrreducibleLoopCheck()) {
379         ReducibilityCheck();
380     }
381     if (IsLogEnabled() && !IsPreAnalysis()) {
382         PrintGraph(std::string("Update CFG With Profile [" + methodName_ + "]").c_str());
383     }
384     BuildCircuit();
385 }
386 
BuildCatchBlocks(const ExceptionInfo & byteCodeException)387 void BytecodeCircuitBuilder::BuildCatchBlocks(const ExceptionInfo &byteCodeException)
388 {
389     // try catch block associate
390     for (size_t i = 0; i < graph_.size(); i++) {
391         auto &bb = RegionAt(i);
392         auto startIndex = bb.start;
393         bool noThrow = true;
394         EnumerateBlock(bb, [&noThrow](const BytecodeInfo &bytecodeInfo) -> bool {
395             if (bytecodeInfo.IsGeneral()) {
396                 if (!bytecodeInfo.NoThrow()) {
397                     noThrow = false;
398                     return false;
399                 }
400             }
401             return true;
402         });
403         if (noThrow) {
404             continue;
405         }
406         const auto pc = pcOffsets_[startIndex];
407         for (auto it = byteCodeException.cbegin(); it != byteCodeException.cend(); it++) {
408             if (pc < it->startPc || pc >= it->endPc) {
409                 continue;
410             }
411             // try block interval
412             const auto &catches = it->catches; // catches start pc
413             for (size_t j = i + 1; j < graph_.size(); j++) {
414                 auto &catchBB = RegionAt(j);
415                 const auto catchStart = pcOffsets_[catchBB.start];
416                 if (std::find(catches.cbegin(), catches.cend(), catchStart) != catches.cend()) {
417                     bb.catches.insert(bb.catches.cbegin(), &catchBB);
418                 }
419             }
420         }
421 
422         // When there are multiple catch blocks in the current block, the set of catch blocks
423         // needs to be sorted to satisfy the order of execution of catch blocks.
424         bb.SortCatches();
425     }
426 }
427 
CollectTryPredsInfo()428 void BytecodeCircuitBuilder::CollectTryPredsInfo()
429 {
430     for (size_t i = 0; i < graph_.size(); i++) {
431         auto &bb = RegionAt(i);
432         if (bb.catches.empty()) {
433             continue;
434         } else if (bb.catches.size() > 1) { // 1: cache size
435             for (auto it = bb.catches.begin() + 1; it != bb.catches.end();) { // 1: invalid catch bb
436                 bb.EraseThisBlock((*it)->trys);
437                 it = bb.catches.erase(it);
438             }
439         }
440 
441         EnumerateBlock(bb, [&bb](const BytecodeInfo &bytecodeInfo) -> bool {
442             if (bytecodeInfo.IsGeneral()) {
443                 // if block which can throw exception has serval catchs block,
444                 // only the innermost catch block is useful
445                 ASSERT(bb.catches.size() == 1); // 1: cache size
446                 if (!bytecodeInfo.NoThrow()) {
447                     bb.catches.at(0)->numOfStatePreds++;
448                 }
449             }
450             return true;
451         });
452     }
453 }
454 
RemoveUnusedPredsInfo(BytecodeRegion & bb,bool skipInsufficientProfile)455 void BytecodeCircuitBuilder::RemoveUnusedPredsInfo(BytecodeRegion& bb, bool skipInsufficientProfile)
456 {
457     EnumerateBlock(bb, [&bb, &skipInsufficientProfile](const BytecodeInfo &bytecodeInfo) -> bool {
458         if (skipInsufficientProfile && bytecodeInfo.IsInsufficientProfile()) {
459             return true;
460         }
461         if (bytecodeInfo.IsGeneral()) {
462             ASSERT(bb.catches.size() == 1); // 1: cache size
463             if (!bytecodeInfo.NoThrow()) {
464                 bb.catches.at(0)->numOfStatePreds--;
465             }
466         }
467         return true;
468     });
469 }
470 
ClearUnreachableRegion(ChunkVector<BytecodeRegion * > & pendingList,bool skipInsufficientProfile)471 void BytecodeCircuitBuilder::ClearUnreachableRegion(ChunkVector<BytecodeRegion*>& pendingList,
472                                                     bool skipInsufficientProfile)
473 {
474     auto bb = pendingList.back();
475     pendingList.pop_back();
476     for (auto it = bb->preds.begin(); it != bb->preds.end(); it++) {
477         ASSERT((*it)->numOfStatePreds >= 0);
478         if ((*it)->numOfStatePreds != 0) {
479             bb->EraseThisBlock((*it)->succs);
480         }
481     }
482     for (auto it = bb->succs.begin(); it != bb->succs.end(); it++) {
483         auto bbNext = *it;
484         ASSERT(bbNext->numOfStatePreds >= 0);
485         if (bbNext->numOfStatePreds != 0) {
486             bb->EraseThisBlock(bbNext->preds);
487             bbNext->numOfStatePreds--;
488             if (bbNext->numOfStatePreds == 0) {
489                 pendingList.emplace_back(bbNext);
490             }
491         }
492     }
493     for (auto it = bb->trys.begin(); it != bb->trys.end(); it++) {
494         ASSERT((*it)->numOfStatePreds >= 0);
495         if ((*it)->numOfStatePreds != 0) {
496             bb->EraseThisBlock((*it)->catches);
497         }
498     }
499     for (auto it = bb->catches.begin(); it != bb->catches.end(); it++) {
500         auto bbNext = *it;
501         ASSERT(bbNext->numOfStatePreds >= 0);
502         if (bbNext->numOfStatePreds != 0) {
503             RemoveUnusedPredsInfo(*bb, skipInsufficientProfile);
504             bb->EraseThisBlock(bbNext->trys);
505             if (bbNext->numOfStatePreds == 0) {
506                 pendingList.emplace_back(bbNext);
507             }
508         }
509     }
510     bb->preds.clear();
511     bb->succs.clear();
512     bb->trys.clear();
513     bb->catches.clear();
514     numOfLiveBB_--;
515 
516     RemoveIfInRpoList(bb);
517 }
518 
RemoveIfInRpoList(BytecodeRegion * bb)519 void BytecodeCircuitBuilder::RemoveIfInRpoList(BytecodeRegion *bb)
520 {
521     auto& rpoList = frameStateBuilder_.GetRpoList();
522     for (auto iter = rpoList.begin(); iter != rpoList.end(); iter++) {
523         if (*iter == bb->id) {
524             rpoList.erase(iter);
525             break;
526         }
527     }
528 }
529 
RemoveUnreachableRegion()530 void BytecodeCircuitBuilder::RemoveUnreachableRegion()
531 {
532     numOfLiveBB_ = graph_.size();
533     ChunkVector<BytecodeRegion*> pendingList(circuit_->chunk());
534     for (size_t i = 1; i < graph_.size(); i++) { // 1: skip entry bb
535         auto &bb = RegionAt(i);
536         ASSERT(bb.numOfStatePreds >= 0);
537         if (bb.numOfStatePreds == 0) {
538             pendingList.emplace_back(&bb);
539         }
540     }
541     while (!pendingList.empty()) {
542         ClearUnreachableRegion(pendingList);
543     }
544 }
545 
RemoveInsufficientProfileRegion()546 void BytecodeCircuitBuilder::RemoveInsufficientProfileRegion()
547 {
548     ChunkVector<BytecodeRegion*> pendingList(circuit_->chunk());
549     for (size_t i = 1; i < graph_.size(); i++) {
550         auto &curBlock = RegionAt(i);
551         const BytecodeInfo& lastBytecodeInfo = GetBytecodeInfo(curBlock.end);
552         // We believe that bytecode with insufficient profile has never been executed.
553         // If the last bytecode in the block has not been executed by the assembly interpreter,
554         // the benefits of compiling its subsequent bytecode are relatively small, for the following reasons:
555         //     1. Even if these bytecode are compiled, they are unlikely to be executed.
556         //     2. These bytecode are missing profile and cannot obtain high-quality machine code.
557         if (!lastBytecodeInfo.IsInsufficientProfile()) {
558             continue;
559         }
560         // 1. Disconnect from the successor.
561         // The last bytecode can only be one of JMP, THOW, or Unexecuted(InsufficientProfile), where JMP and THOW must
562         // have been executed. The Block split from Unexecuted(InsufficientProfile) has only one successor or none.
563         ASSERT(curBlock.succs.size() <= 1);
564         for (auto it = curBlock.succs.begin(); it != curBlock.succs.end(); it++) {
565             auto nextBlock = *it;
566             ASSERT(nextBlock->numOfStatePreds >= 0);
567             if (nextBlock->numOfStatePreds == 0) {
568                 continue;
569             }
570             curBlock.EraseThisBlock(nextBlock->preds);
571             nextBlock->numOfStatePreds--;
572             if (nextBlock->numOfStatePreds == 0) {
573                 pendingList.emplace_back(nextBlock);
574             }
575         }
576         curBlock.succs.clear();
577         // 2. Disconnect from the catch.
578         // If the bytecode has not been executed(InsufficientProfile), a deopt is generated.
579         // The assembly interpreter continues to handle the exception.
580         if (curBlock.catches.size() == 0 || lastBytecodeInfo.NoThrow()) {
581             continue;
582         }
583         ASSERT(curBlock.catches.size() == 1);
584         curBlock.catches.at(0)->numOfStatePreds--;
585         // Before lastBytecodeInfo, there may still be bytecode that throws an exception,
586         // which will determine whether the catchBlock can be removed from the catchs list of curBlock.
587         size_t numOfThrowBytecode = 0;
588         EnumerateBlock(curBlock, [&numOfThrowBytecode](const BytecodeInfo &bytecodeInfo) -> bool {
589             if (bytecodeInfo.IsGeneral() && !bytecodeInfo.NoThrow()) {
590                 numOfThrowBytecode++;
591             }
592             return true;
593         });
594         // no other throw
595         if (numOfThrowBytecode == 1) {
596             curBlock.EraseThisBlock(curBlock.catches.at(0)->trys);
597             if (curBlock.catches.at(0)->numOfStatePreds == 0) {
598                 pendingList.emplace_back(curBlock.catches.at(0));
599             }
600             curBlock.catches.clear();
601         }
602     }
603     while (!pendingList.empty()) {
604         ClearUnreachableRegion(pendingList, true);
605     }
606     RemoveIsolatedRegion();
607 }
608 
MarkConnection(const BytecodeRegion & curBlock,std::vector<bool> & connectedToRoot)609 void MarkConnection(const BytecodeRegion& curBlock, std::vector<bool>& connectedToRoot)
610 {
611     ASSERT(curBlock.id < connectedToRoot.size());
612     if (connectedToRoot[curBlock.id]) {
613         return;
614     }
615     connectedToRoot[curBlock.id] = true;
616     for (auto it = curBlock.succs.begin(); it != curBlock.succs.end(); it++) {
617         BytecodeRegion* nextBlock = *it;
618         MarkConnection(*nextBlock, connectedToRoot);
619     }
620     if (curBlock.catches.size() == 0) {
621         return;
622     }
623     ASSERT(curBlock.catches.size() == 1);
624     MarkConnection(*(curBlock.catches.at(0)), connectedToRoot);
625 }
626 
RemoveIsolatedRegion()627 void BytecodeCircuitBuilder::RemoveIsolatedRegion()
628 {
629     std::vector<bool> connectedToRoot(graph_.size(), false);
630     MarkConnection(RegionAt(0), connectedToRoot);
631     ChunkVector<BytecodeRegion*> pendingList(circuit_->chunk());
632     for (size_t index = 0; index < connectedToRoot.size(); ++index) {
633         auto& curBlock = RegionAt(index);
634         if (!connectedToRoot[index] && curBlock.numOfStatePreds != 0) {
635             curBlock.numOfStatePreds = 0;
636             pendingList.emplace_back(&curBlock);
637         }
638     }
639     while (!pendingList.empty()) {
640         ClearUnreachableRegion(pendingList, true);
641     }
642 }
643 
ComputeNumOfLoopBack()644 void BytecodeCircuitBuilder::ComputeNumOfLoopBack()
645 {
646     for (size_t i = 0; i < graph_.size(); i++) {
647         auto &bb = RegionAt(i);
648         if (!IsEntryBlock(bb.id) && bb.numOfStatePreds == 0) {
649             continue;
650         }
651         for (auto &succ: bb.succs) {
652             if (succ->IsLoopBack(bb)) {
653                 succ->numOfLoopBack++;
654             }
655         }
656         if (bb.catches.empty()) {
657             continue;
658         }
659 
660         EnumerateBlock(bb, [&bb](const BytecodeInfo &bytecodeInfo) -> bool {
661             if (bytecodeInfo.IsGeneral() && !bytecodeInfo.NoThrow() && bb.catches.at(0)->IsLoopBack(bb)) {
662                 // if block which can throw exception has serval catchs block,
663                 // only the innermost catch block is useful
664                 ASSERT(bb.catches.size() == 1); // 1: cache size
665                 bb.catches.at(0)->numOfLoopBack++;
666             }
667             return true;
668         });
669     }
670 }
671 // Update CFG's predecessor, successor and try catch associations
UpdateCFG()672 void BytecodeCircuitBuilder::UpdateCFG()
673 {
674     for (size_t i = 0; i < graph_.size(); i++) {
675         auto &bb = RegionAt(i);
676         bb.preds.clear();
677         bb.trys.clear();
678     }
679     for (size_t i = 0; i < graph_.size(); i++) {
680         auto &bb = RegionAt(i);
681         for (auto &succ: bb.succs) {
682             succ->preds.emplace_back(&bb);
683             succ->numOfStatePreds++;
684         }
685         for (auto &catchBlock: bb.catches) {
686             catchBlock->trys.emplace_back(&bb);
687         }
688     }
689 }
690 
691 // build circuit
BuildCircuitArgs()692 void BytecodeCircuitBuilder::BuildCircuitArgs()
693 {
694     argAcc_.NewCommonArg(CommonArgIdx::GLUE, MachineType::I64, GateType::NJSValue());
695     if (!method_->IsFastCall()) {
696         argAcc_.NewCommonArg(CommonArgIdx::ACTUAL_ARGC, MachineType::I64, GateType::NJSValue());
697         argAcc_.NewCommonArg(CommonArgIdx::ACTUAL_ARGV, MachineType::ARCH, GateType::NJSValue());
698         auto funcIdx = static_cast<size_t>(CommonArgIdx::FUNC);
699         const size_t actualNumArgs = argAcc_.GetActualNumArgs();
700         // new actual argument gates
701         for (size_t argIdx = funcIdx; argIdx < actualNumArgs; argIdx++) {
702             argAcc_.NewArg(argIdx);
703         }
704     } else {
705         auto funcIdx = static_cast<size_t>(FastCallArgIdx::FUNC);
706         size_t actualNumArgs = static_cast<size_t>(FastCallArgIdx::NUM_OF_ARGS) + method_->GetNumArgsWithCallField();
707         for (size_t argIdx = funcIdx; argIdx < actualNumArgs; argIdx++) {
708             argAcc_.NewArg(argIdx);
709         }
710     }
711     argAcc_.CollectArgs();
712     BuildFrameArgs();
713 }
714 
BuildFrameArgs()715 void BytecodeCircuitBuilder::BuildFrameArgs()
716 {
717     UInt32PairAccessor accessor(0, 0);
718     auto metaData = circuit_->FrameArgs(accessor.ToValue());
719     size_t numArgs = metaData->GetNumIns();
720     std::vector<GateRef> args(numArgs, Circuit::NullGate());
721     size_t idx = 0;
722     args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::FUNC);
723     args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::NEW_TARGET);
724     args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::THIS_OBJECT);
725     args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::ACTUAL_ARGC);
726     args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::ACTUAL_ARGV);
727     GateRef sharedConstpool = Circuit::NullGate();
728     GateRef unSharedConstpool = Circuit::NullGate();
729     GetCurrentConstpool(argAcc_.GetCommonArgGate(CommonArgIdx::FUNC), sharedConstpool, unSharedConstpool);
730     args[idx++] = sharedConstpool;
731     args[idx++] = unSharedConstpool;
732     args[idx++] = GetPreFrameArgs();
733     GateRef frameArgs = circuit_->NewGate(metaData, args);
734     argAcc_.SetFrameArgs(frameArgs);
735 }
736 
BuildOSRArgs()737 void BytecodeCircuitBuilder::BuildOSRArgs()
738 {
739     // offset -1 : glue
740     (void)circuit_->NewGate(circuit_->GetMetaBuilder()->InitVreg(INIT_VRGE_GLUE), MachineType::I64,
741                             {circuit_->GetArgRoot()}, GateType::NJSValue());
742     // offset -2 : argc
743     GateRef argc = method_->IsFastCall()
744                        ? circuit_->GetConstantGate(MachineType::I64, 0, GateType::NJSValue())
745                        : circuit_->NewGate(circuit_->GetMetaBuilder()->InitVreg(INIT_VRGE_ARGS), MachineType::I64,
746                                            {circuit_->GetArgRoot()}, GateType::TaggedValue());
747     // offset -3 : argv
748     GateRef argv = method_->IsFastCall()
749                        ? circuit_->GetConstantGate(MachineType::ARCH, 0, GateType::NJSValue())
750                        : circuit_->NewGate(circuit_->GetMetaBuilder()->InitVreg(INIT_VRGE_ARGV), MachineType::I64,
751                                            {circuit_->GetArgRoot()}, GateType::TaggedValue());
752     // offset -4 : func
753     (void)circuit_->NewGate(circuit_->GetMetaBuilder()->InitVreg(INIT_VRGE_FUNCTION), MachineType::I64,
754                             {circuit_->GetArgRoot()}, GateType::TaggedValue());
755     // offset -5 : new_target
756     GateRef newTarget =
757         method_->IsFastCall()
758             ? circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_UNDEFINED, GateType::UndefinedType())
759             : circuit_->NewGate(circuit_->GetMetaBuilder()->InitVreg(INIT_VRGE_NEW_TARGET), MachineType::I64,
760                                 {circuit_->GetArgRoot()}, GateType::TaggedValue());
761     // offset -6 : this_object
762     (void)circuit_->NewGate(circuit_->GetMetaBuilder()->InitVreg(INIT_VRGE_THIS_OBJECT), MachineType::I64,
763                             {circuit_->GetArgRoot()}, GateType::TaggedValue());
764     // offset -7 : numargs
765     (void)circuit_->NewGate(circuit_->GetMetaBuilder()->InitVreg(INIT_VRGE_NUM_ARGS), MachineType::I64,
766                             {circuit_->GetArgRoot()}, GateType::TaggedValue());
767     for (size_t argIdx = 1; argIdx <= method_->GetNumArgsWithCallField(); argIdx++) {
768         // common args
769         argAcc_.NewArg(method_->IsFastCall() ? static_cast<size_t>(FastCallArgIdx::NUM_OF_ARGS)
770                                              : static_cast<size_t>(CommonArgIdx::NUM_OF_ARGS) + argIdx);
771     }
772 
773     auto &args = argAcc_.args_;
774     if (args.size() == 0) {
775         GateAccessor(circuit_).GetArgsOuts(args);
776         std::reverse(args.begin(), args.end());
777         if (method_->IsFastCall() && args.size() >= static_cast<uint8_t>(FastCallArgIdx::NUM_OF_ARGS)) {
778             args.insert(args.begin() + static_cast<uint8_t>(CommonArgIdx::ACTUAL_ARGC), argc);
779             args.insert(args.begin() + static_cast<uint8_t>(CommonArgIdx::ACTUAL_ARGV), argv);
780             // 3: newtarget index
781             args.insert(args.begin() + static_cast<uint8_t>(CommonArgIdx::NEW_TARGET), newTarget);
782         }
783     }
784 
785     BuildFrameArgs();
786 }
787 
NewDeopt(BytecodeRegion & bb)788 GateRef BytecodeCircuitBuilder::NewDeopt(BytecodeRegion &bb)
789 {
790     ASSERT(bb.succs.empty());
791     auto &iterator = bb.GetBytecodeIterator();
792     const BytecodeInfo &bytecodeInfo = iterator.GetBytecodeInfo();
793     GateRef state = frameStateBuilder_.GetCurrentState();
794     GateRef depend = frameStateBuilder_.GetCurrentDepend();
795     GateRef frameState = gateAcc_.FindNearestFrameState(depend);
796     if (bytecodeInfo.NeedFrameStateInPlace()) {
797         frameState = frameStateBuilder_.GetBcFrameStateCache();
798     }
799     std::string comment = Deoptimizier::DisplayItems(DeoptType::INSUFFICIENTPROFILE);
800     GateRef type = circuit_->GetConstantGate(MachineType::I64, static_cast<int64_t>(DeoptType::INSUFFICIENTPROFILE),
801                                              GateType::NJSValue());
802     GateRef condition = circuit_->GetConstantGate(MachineType::I1, 0, GateType::NJSValue());
803     GateRef deopt = circuit_->NewGate(circuit_->DeoptCheck(), MachineType::I1,
804                                       {state, depend, condition, frameState, type},
805                                       GateType::NJSValue(), comment.c_str());
806     GateRef dependRelay = circuit_->NewGate(circuit_->DependRelay(), {deopt, depend});
807     GateRef undef =
808         circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_UNDEFINED, GateType::TaggedValue());
809     circuit_->NewGate(circuit_->Return(), {deopt, dependRelay, undef, circuit_->GetReturnRoot()});
810     byteCodeToJSGates_[iterator.Index()].emplace_back(deopt);
811     jsGatesToByteCode_[deopt] = iterator.Index();
812     return deopt;
813 }
814 
CreateGateInList(const BytecodeInfo & info,const GateMetaData * meta)815 std::vector<GateRef> BytecodeCircuitBuilder::CreateGateInList(
816     const BytecodeInfo &info, const GateMetaData *meta)
817 {
818     auto numValues = meta->GetNumIns();
819     const size_t length = meta->GetInValueStarts();
820     std::vector<GateRef> inList(numValues, Circuit::NullGate());
821     auto inputSize = info.inputs.size();
822     for (size_t i = 0; i < inputSize; i++) {
823         auto &input = info.inputs[i];
824         if (std::holds_alternative<ConstDataId>(input)) {
825             inList[i + length] = circuit_->GetConstantGate(MachineType::I64,
826                                                            std::get<ConstDataId>(input).GetId(),
827                                                            GateType::NJSValue());
828         } else if (std::holds_alternative<Immediate>(input)) {
829             inList[i + length] = circuit_->GetConstantGate(MachineType::I64,
830                                                            std::get<Immediate>(input).GetValue(),
831                                                            GateType::NJSValue());
832         } else if (std::holds_alternative<ICSlotId>(input)) {
833             inList[i + length] = circuit_->GetConstantGate(MachineType::I16,
834                                                            std::get<ICSlotId>(input).GetId(),
835                                                            GateType::NJSValue());
836         } else {
837             ASSERT(std::holds_alternative<VirtualRegister>(input));
838             continue;
839         }
840     }
841     if (info.AccIn()) {
842         inputSize++;
843     }
844     if (meta->HasFrameState()) {
845         inList[inputSize + length] = GetFrameArgs();
846     }
847     return inList;
848 }
849 
NewConst(const BytecodeInfo & info)850 GateRef BytecodeCircuitBuilder::NewConst(const BytecodeInfo &info)
851 {
852     auto opcode = info.GetOpcode();
853     GateRef gate = 0;
854     switch (opcode) {
855         case EcmaOpcode::LDNAN:
856             gate = circuit_->GetConstantGate(MachineType::I64,
857                                              base::NumberHelper::GetNaN(),
858                                              GateType::NumberType());
859             break;
860         case EcmaOpcode::LDINFINITY:
861             gate = circuit_->GetConstantGate(MachineType::I64,
862                                              base::NumberHelper::GetPositiveInfinity(),
863                                              GateType::NumberType());
864             break;
865         case EcmaOpcode::LDUNDEFINED:
866             gate = circuit_->GetConstantGate(MachineType::I64,
867                                              JSTaggedValue::VALUE_UNDEFINED,
868                                              GateType::UndefinedType());
869             break;
870         case EcmaOpcode::LDNULL:
871             gate = circuit_->GetConstantGate(MachineType::I64,
872                                              JSTaggedValue::VALUE_NULL,
873                                              GateType::NullType());
874             break;
875         case EcmaOpcode::LDTRUE:
876             gate = circuit_->GetConstantGate(MachineType::I64,
877                                              JSTaggedValue::VALUE_TRUE,
878                                              GateType::BooleanType());
879             break;
880         case EcmaOpcode::LDFALSE:
881             gate = circuit_->GetConstantGate(MachineType::I64,
882                                              JSTaggedValue::VALUE_FALSE,
883                                              GateType::BooleanType());
884             break;
885         case EcmaOpcode::LDHOLE:
886             gate = circuit_->GetConstantGate(MachineType::I64,
887                                              JSTaggedValue::VALUE_HOLE,
888                                              GateType::TaggedValue());
889             break;
890         case EcmaOpcode::LDAI_IMM32:
891             gate = circuit_->GetConstantGate(MachineType::I64,
892                                              std::get<Immediate>(info.inputs[0]).ToJSTaggedValueInt(),
893                                              GateType::IntType());
894             break;
895         case EcmaOpcode::FLDAI_IMM64:
896             gate = circuit_->GetConstantGate(MachineType::I64,
897                                              std::get<Immediate>(info.inputs.at(0)).ToJSTaggedValueDouble(),
898                                              GateType::DoubleType());
899             break;
900         case EcmaOpcode::LDFUNCTION:
901             gate = argAcc_.GetCommonArgGate(CommonArgIdx::FUNC);
902             break;
903         case EcmaOpcode::LDNEWTARGET:
904             gate = argAcc_.GetCommonArgGate(CommonArgIdx::NEW_TARGET);
905             break;
906         case EcmaOpcode::LDTHIS:
907             gate = argAcc_.GetCommonArgGate(CommonArgIdx::THIS_OBJECT);
908             break;
909         default:
910             LOG_ECMA(FATAL) << "this branch is unreachable";
911             UNREACHABLE();
912     }
913     return gate;
914 }
915 
MergeThrowGate(BytecodeRegion & bb,uint32_t bcIndex)916 void BytecodeCircuitBuilder::MergeThrowGate(BytecodeRegion &bb, uint32_t bcIndex)
917 {
918     auto state = frameStateBuilder_.GetCurrentState();
919     auto depend = frameStateBuilder_.GetCurrentDepend();
920     if (!bb.catches.empty()) {
921         auto ifSuccess = circuit_->NewGate(circuit_->IfSuccess(), {state});
922         auto dependRelay = circuit_->NewGate(circuit_->DependRelay(), {ifSuccess, depend});
923         auto ifException = circuit_->NewGate(circuit_->IfException(), {state, depend});
924         frameStateBuilder_.UpdateStateDepend(ifException, ifException);
925         ASSERT(bb.catches.size() == 1); // 1: one catch
926         auto bbNext = bb.catches.at(0);
927         frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
928         bbNext->expandedPreds.push_back({bb.id, bcIndex, true});
929         state = ifSuccess;
930         depend = dependRelay;
931     }
932     auto constant = circuit_->GetConstantGate(MachineType::I64,
933                                               JSTaggedValue::VALUE_EXCEPTION,
934                                               GateType::TaggedValue());
935     circuit_->NewGate(circuit_->Return(),
936         { state, depend, constant, circuit_->GetReturnRoot() });
937 }
938 
MergeExceptionGete(BytecodeRegion & bb,const BytecodeInfo & bytecodeInfo,uint32_t bcIndex)939 void BytecodeCircuitBuilder::MergeExceptionGete(BytecodeRegion &bb,
940     const BytecodeInfo& bytecodeInfo, uint32_t bcIndex)
941 {
942     auto state = frameStateBuilder_.GetCurrentState();
943     auto depend = frameStateBuilder_.GetCurrentDepend();
944     auto ifSuccess = circuit_->NewGate(circuit_->IfSuccess(), {state});
945     auto dependRelay = circuit_->NewGate(circuit_->DependRelay(), {ifSuccess, depend});
946     ASSERT(bb.catches.size() == 1); // 1: one catch
947     auto bbNext = bb.catches.at(0);
948     auto ifException = circuit_->NewGate(circuit_->IfException(), {state, depend});
949     frameStateBuilder_.UpdateStateDepend(ifException, ifException);
950     frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
951     if (bytecodeInfo.GetOpcode() == EcmaOpcode::CREATEASYNCGENERATOROBJ_V8) {
952         bbNext->expandedPreds.push_back({bb.id, bcIndex + 1, true}); // 1: next pc
953     } else {
954         bbNext->expandedPreds.push_back({bb.id, bcIndex, true});
955     }
956     depend = dependRelay;
957     frameStateBuilder_.UpdateStateDepend(ifSuccess, depend);
958 }
959 
NewJSGate(BytecodeRegion & bb)960 void BytecodeCircuitBuilder::NewJSGate(BytecodeRegion &bb)
961 {
962     auto &iterator = bb.GetBytecodeIterator();
963     const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo();
964     GateRef state = frameStateBuilder_.GetCurrentState();
965     GateRef depend = frameStateBuilder_.GetCurrentDepend();
966     size_t numValueInputs = bytecodeInfo.ComputeValueInputCount();
967     GateRef gate = 0;
968     bool writable = !bytecodeInfo.NoSideEffects();
969     bool hasFrameState = bytecodeInfo.HasFrameState();
970     size_t pcOffset = GetPcOffset(iterator.Index());
971     auto methodOffset = method_->GetMethodId().GetOffset();
972     auto meta = circuit_->JSBytecode(
973         numValueInputs, methodOffset, bytecodeInfo.GetOpcode(), pcOffset, iterator.Index(), writable, hasFrameState);
974     std::vector<GateRef> inList = CreateGateInList(bytecodeInfo, meta);
975     if (bytecodeInfo.IsDef()) {
976         gate = circuit_->NewGate(meta, MachineType::I64, inList.size(),
977                                  inList.data(), GateType::AnyType());
978     } else {
979         gate = circuit_->NewGate(meta, MachineType::NOVALUE, inList.size(),
980                                  inList.data(), GateType::Empty());
981     }
982     byteCodeToJSGates_[iterator.Index()].emplace_back(gate);
983     jsGatesToByteCode_[gate] = iterator.Index();
984     gateAcc_.NewIn(gate, 0, state);
985     gateAcc_.NewIn(gate, 1, depend);
986     frameStateBuilder_.UpdateStateDepend(gate, gate);
987     frameStateBuilder_.UpdateFrameValues(bytecodeInfo, iterator.Index(), gate);
988     if (bytecodeInfo.IsThrow()) {
989         MergeThrowGate(bb, iterator.Index());
990         return;
991     }
992 
993     if (!bb.catches.empty() && !bytecodeInfo.NoThrow()) {
994         MergeExceptionGete(bb, bytecodeInfo, iterator.Index());
995     } else if (!bb.catches.empty()) {
996         frameStateBuilder_.GetOrOCreateMergedContext(bb.catches.at(0)->id);
997     }
998     if (bytecodeInfo.IsGeneratorRelative()) {
999         suspendAndResumeGates_.emplace_back(gate);
1000     }
1001 }
1002 
NewJump(BytecodeRegion & bb)1003 void BytecodeCircuitBuilder::NewJump(BytecodeRegion &bb)
1004 {
1005     auto &iterator = bb.GetBytecodeIterator();
1006     const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo();
1007     GateRef state = frameStateBuilder_.GetCurrentState();
1008     GateRef depend = frameStateBuilder_.GetCurrentDepend();
1009     size_t numValueInputs = bytecodeInfo.ComputeValueInputCount();
1010     if (bytecodeInfo.IsCondJump() && bb.succs.size() == 2) { // 2: two succ
1011         size_t pcOffset = GetPcOffset(iterator.Index());
1012         auto methodOffset = method_->GetMethodId().GetOffset();
1013         auto meta = circuit_->JSBytecode(
1014             numValueInputs, methodOffset, bytecodeInfo.GetOpcode(), pcOffset, iterator.Index(), false, false);
1015         auto numValues = meta->GetNumIns();
1016         GateRef gate = circuit_->NewGate(meta, std::vector<GateRef>(numValues, Circuit::NullGate()));
1017         gateAcc_.NewIn(gate, 0, state);
1018         gateAcc_.NewIn(gate, 1, depend);
1019         frameStateBuilder_.UpdateStateDepend(gate, gate);
1020         frameStateBuilder_.UpdateFrameValues(bytecodeInfo, iterator.Index(), gate);
1021 
1022         auto ifTrue = circuit_->NewGate(circuit_->IfTrue(), {gate});
1023         auto trueRelay = circuit_->NewGate(circuit_->DependRelay(), {ifTrue, gate});
1024         auto ifFalse = circuit_->NewGate(circuit_->IfFalse(), {gate});
1025         auto falseRelay = circuit_->NewGate(circuit_->DependRelay(), {ifFalse, gate});
1026         for (auto &bbNext: bb.succs) {
1027             if (iterator.Index() + 1 == bbNext->start) {
1028                 frameStateBuilder_.UpdateStateDepend(ifFalse, falseRelay);
1029                 frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
1030                 bbNext->expandedPreds.push_back({bb.id, iterator.Index(), false});
1031             } else {
1032                 frameStateBuilder_.UpdateStateDepend(ifTrue, trueRelay);
1033                 frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
1034                 bbNext->expandedPreds.push_back({bb.id, iterator.Index(), false});
1035             }
1036         }
1037         byteCodeToJSGates_[iterator.Index()].emplace_back(gate);
1038         jsGatesToByteCode_[gate] = iterator.Index();
1039     } else {
1040         ASSERT(bb.succs.size() == 1); // 1: only one succ if not condjump
1041         auto &bbNext = bb.succs.at(0);
1042         frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
1043         bbNext->expandedPreds.push_back({bb.id, iterator.Index(), false});
1044     }
1045 
1046     if (!bb.catches.empty()) {
1047         frameStateBuilder_.GetOrOCreateMergedContext(bb.catches.at(0)->id);
1048     }
1049 }
1050 
NewReturn(BytecodeRegion & bb)1051 GateRef BytecodeCircuitBuilder::NewReturn(BytecodeRegion &bb)
1052 {
1053     ASSERT(bb.succs.empty());
1054     auto &iterator = bb.GetBytecodeIterator();
1055     const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo();
1056     GateRef state = frameStateBuilder_.GetCurrentState();
1057     GateRef depend = frameStateBuilder_.GetCurrentDepend();
1058     GateRef gate = Circuit::NullGate();
1059     if (bytecodeInfo.GetOpcode() == EcmaOpcode::RETURN) {
1060         // handle return.dyn bytecode
1061         gate = circuit_->NewGate(circuit_->Return(),
1062             { state, depend, Circuit::NullGate(), circuit_->GetReturnRoot() });
1063         byteCodeToJSGates_[iterator.Index()].emplace_back(gate);
1064         jsGatesToByteCode_[gate] = iterator.Index();
1065     } else if (bytecodeInfo.GetOpcode() == EcmaOpcode::RETURNUNDEFINED) {
1066         // handle returnundefined bytecode
1067         auto constant = circuit_->GetConstantGate(MachineType::I64,
1068                                                   JSTaggedValue::VALUE_UNDEFINED,
1069                                                   GateType::TaggedValue());
1070         gate = circuit_->NewGate(circuit_->Return(),
1071             { state, depend, constant, circuit_->GetReturnRoot() });
1072         byteCodeToJSGates_[iterator.Index()].emplace_back(gate);
1073         jsGatesToByteCode_[gate] = iterator.Index();
1074     }
1075     return gate;
1076 }
1077 
NewByteCode(BytecodeRegion & bb)1078 void BytecodeCircuitBuilder::NewByteCode(BytecodeRegion &bb)
1079 {
1080     auto &iterator = bb.GetBytecodeIterator();
1081     const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo();
1082     FrameLiveOut* liveout;
1083     auto bcId = iterator.Index();
1084     if (bcId != 0 && iterator.IsInRange(bcId - 1)) {
1085         liveout = frameStateBuilder_.GetOrOCreateBCEndLiveOut(bcId - 1);
1086     } else {
1087         liveout = frameStateBuilder_.GetOrOCreateBBLiveOut(bb.id);
1088     }
1089     frameStateBuilder_.AdvanceToNextBc(bytecodeInfo, liveout, bcId);
1090     GateRef gate = Circuit::NullGate();
1091     if (bytecodeInfo.IsInsufficientProfile()) {
1092         // handle general ecma.* bytecodes
1093         NewDeopt(bb);
1094     } else if (bytecodeInfo.IsSetConstant()) {
1095         // handle bytecode command to get constants
1096         gate = NewConst(bytecodeInfo);
1097         byteCodeToJSGates_[iterator.Index()].emplace_back(gate);
1098         jsGatesToByteCode_[gate] = iterator.Index();
1099     } else if (bytecodeInfo.IsGeneral()) {
1100         // handle general ecma.* bytecodes
1101         NewJSGate(bb);
1102     } else if (bytecodeInfo.IsJump()) {
1103         // handle conditional jump and unconditional jump bytecodes
1104         NewJump(bb);
1105     } else if (bytecodeInfo.IsReturn()) {
1106         // handle return.dyn and returnundefined bytecodes
1107         gate = NewReturn(bb);
1108     } else if (bytecodeInfo.IsMov()) {
1109         frameStateBuilder_.UpdateMoveValues(bytecodeInfo);
1110     } else if (!bytecodeInfo.IsDiscarded()) {
1111         LOG_ECMA(FATAL) << "this branch is unreachable";
1112         UNREACHABLE();
1113     }
1114     if (gate != Circuit::NullGate()) {
1115         frameStateBuilder_.UpdateFrameValues(bytecodeInfo, iterator.Index(), gate);
1116     }
1117 }
1118 
BuildSubCircuit()1119 void BytecodeCircuitBuilder::BuildSubCircuit()
1120 {
1121     auto &entryBlock = RegionAt(0);
1122     frameStateBuilder_.InitEntryBB(entryBlock);
1123     auto& rpoList = frameStateBuilder_.GetRpoList();
1124     for (auto &bbId: rpoList) {
1125         auto &bb = RegionAt(bbId);
1126         frameStateBuilder_.AdvanceToNextBB(bb);
1127         if (IsEntryBlock(bb.id)) {
1128             if (NeedCheckSafePointAndStackOver()) {
1129                 GateRef state = frameStateBuilder_.GetCurrentState();
1130                 GateRef depend = frameStateBuilder_.GetCurrentDepend();
1131                 auto stackCheck = circuit_->NewGate(circuit_->CheckSafePointAndStackOver(), {state, depend});
1132                 bb.dependCache = stackCheck;
1133                 frameStateBuilder_.UpdateStateDepend(stackCheck, stackCheck);
1134             }
1135             auto &bbNext = RegionAt(bb.id + 1);
1136             frameStateBuilder_.MergeIntoSuccessor(bb, bbNext);
1137             bbNext.expandedPreds.push_back({bb.id, bb.end, false});
1138             continue;
1139         }
1140         EnumerateBlock(bb, [this, &bb]
1141             (const BytecodeInfo &bytecodeInfo) -> bool {
1142             NewByteCode(bb);
1143             if (bytecodeInfo.IsJump() || bytecodeInfo.IsThrow()) {
1144                 return false;
1145             }
1146             return true;
1147         });
1148         bool needFallThrough = true;
1149         if (!bb.IsEmptryBlock()) {
1150             const BytecodeInfo& bytecodeInfo = GetBytecodeInfo(bb.end);
1151             needFallThrough = bytecodeInfo.needFallThrough();
1152         }
1153         // fallThrough or empty merge bb
1154         if (needFallThrough) {
1155             ASSERT(bb.succs.size() == 1); // 1: fall through
1156             auto &bbNext = RegionAt(bb.succs[0]->id);
1157             frameStateBuilder_.MergeIntoSuccessor(bb, bbNext);
1158             bbNext.expandedPreds.push_back({bb.id, bb.end, false});
1159         }
1160     }
1161 }
1162 
FindOsrLoopHeadBB()1163 bool BytecodeCircuitBuilder::FindOsrLoopHeadBB()
1164 {
1165     int32_t loopBackBcIndex {-1};
1166     for (size_t k = 0; k < pcOffsets_.size(); k++) {
1167         if (static_cast<int32_t>(pcOffsets_[k] - pcOffsets_[0]) == osrOffset_) {
1168             loopBackBcIndex = static_cast<int32_t>(k);
1169         }
1170     }
1171     if (loopBackBcIndex == -1) {
1172         LOG_COMPILER(ERROR) << "Unable to find the loop back of OSR.";
1173         return false;
1174     }
1175     auto &rpoList = frameStateBuilder_.GetRpoList();
1176     for (auto &bbId : rpoList) {
1177         auto &bb = RegionAt(bbId);
1178         if (bb.end == static_cast<uint32_t>(loopBackBcIndex)) {
1179             frameStateBuilder_.SetOsrLoopHeadBB(bb);
1180             return true;
1181         }
1182     }
1183     LOG_COMPILER(ERROR) << "Unable to find the loop head bb of OSR.";
1184     return false;
1185 }
1186 
GenDeoptAndReturnForOsrLoopExit(BytecodeRegion & osrLoopExitBB)1187 void BytecodeCircuitBuilder::GenDeoptAndReturnForOsrLoopExit(BytecodeRegion &osrLoopExitBB)
1188 {
1189     frameStateBuilder_.AdvanceToNextBB(osrLoopExitBB, true);
1190     GateRef state = frameStateBuilder_.GetCurrentState();
1191     GateRef depend = frameStateBuilder_.GetCurrentDepend();
1192     std::string comment = Deoptimizier::DisplayItems(DeoptType::OSRLOOPEXIT);
1193     GateRef type =
1194         circuit_->GetConstantGate(MachineType::I64, static_cast<int64_t>(DeoptType::OSRLOOPEXIT), GateType::NJSValue());
1195     GateRef condition = circuit_->GetConstantGate(MachineType::I1, 0, GateType::NJSValue());
1196     GateRef deopt = circuit_->NewGate(circuit_->DeoptCheck(), MachineType::I1,
1197                                       {state, depend, condition, gateAcc_.GetFrameState(depend), type},
1198                                       GateType::NJSValue(), comment.c_str());
1199     GateRef dependRelay = circuit_->NewGate(circuit_->DependRelay(), {deopt, depend});
1200     GateRef undef =
1201         circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_UNDEFINED, GateType::TaggedValue());
1202     circuit_->NewGate(circuit_->Return(), {state, dependRelay, undef, circuit_->GetReturnRoot()});
1203 }
1204 
CollectCacheBBforOSRLoop(BytecodeRegion * bb)1205 void BytecodeCircuitBuilder::CollectCacheBBforOSRLoop(BytecodeRegion *bb)
1206 {
1207     catchBBOfOSRLoop_.insert(bb);
1208     for (BytecodeRegion *succBB : bb->succs) {
1209         CollectCacheBBforOSRLoop(succBB);
1210     }
1211 }
1212 
HandleOsrLoopBody(BytecodeRegion & osrLoopBodyBB)1213 void BytecodeCircuitBuilder::HandleOsrLoopBody(BytecodeRegion &osrLoopBodyBB)
1214 {
1215     if (!osrLoopBodyBB.trys.empty()) {
1216         GateRef state = frameStateBuilder_.GetCurrentState();
1217         GateRef depend = frameStateBuilder_.GetCurrentDepend();
1218         auto getException =
1219             circuit_->NewGate(circuit_->GetException(), MachineType::I64, {state, depend}, GateType::AnyType());
1220         frameStateBuilder_.UpdateAccumulator(getException);
1221         frameStateBuilder_.UpdateStateDepend(state, getException);
1222     }
1223     // collect catch BB.
1224     if (!osrLoopBodyBB.catches.empty()) {
1225         for (BytecodeRegion *targetBB : osrLoopBodyBB.catches) {
1226             CollectCacheBBforOSRLoop(targetBB);
1227         }
1228     }
1229     EnumerateBlock(osrLoopBodyBB, [this, &osrLoopBodyBB](const BytecodeInfo &bytecodeInfo) -> bool {
1230         NewByteCode(osrLoopBodyBB);
1231         if (bytecodeInfo.IsJump() || bytecodeInfo.IsThrow()) {
1232             return false;
1233         }
1234         return true;
1235     });
1236     bool needFallThrough = true;
1237     if (!osrLoopBodyBB.IsEmptryBlock()) {
1238         const BytecodeInfo &bytecodeInfo = GetBytecodeInfo(osrLoopBodyBB.end);
1239         needFallThrough = bytecodeInfo.needFallThrough();
1240     }
1241     // fallThrough or empty merge osrLoopBodyBB
1242     if (needFallThrough) {
1243         ASSERT(osrLoopBodyBB.succs.size() == 1);  // 1: fall through
1244         auto &bbNext = RegionAt(osrLoopBodyBB.succs[0]->id);
1245         frameStateBuilder_.MergeIntoSuccessor(osrLoopBodyBB, bbNext);
1246         bbNext.expandedPreds.push_back({osrLoopBodyBB.id, osrLoopBodyBB.end, false});
1247     }
1248 }
1249 
BuildOsrCircuit()1250 void BytecodeCircuitBuilder::BuildOsrCircuit()
1251 {
1252     if (!FindOsrLoopHeadBB()) {
1253         LOG_COMPILER(FATAL) << "invalid osr offset";
1254     }
1255     circuit_->SetIsOsr();
1256     auto &entryBlock = RegionAt(0);
1257     frameStateBuilder_.InitEntryBB(entryBlock);
1258     std::set<size_t> osrLoopExitBBIds;
1259     auto &rpoList = frameStateBuilder_.GetRpoList();
1260     for (auto &bbId : rpoList) {
1261         auto &bb = RegionAt(bbId);
1262         if (frameStateBuilder_.IsOsrLoopExit(bb)) {
1263             // The loop exit BB is in front of the loop head BB. At this time,
1264             // the loop exit BB does not have the context object, and the processing of the loop exit BB is delayed.
1265             if (frameStateBuilder_.IsContextExists(bb.id)) {
1266                 GenDeoptAndReturnForOsrLoopExit(bb);
1267             } else {
1268                 osrLoopExitBBIds.insert(bbId);
1269             }
1270             continue;
1271         }
1272 
1273         // Processes only the BBs related to the loop specified by the OSR.
1274         if (!IsEntryBlock(bb.id) && frameStateBuilder_.OutOfOsrLoop(bb) && !IsCacheBBOfOSRLoop(bb)) {
1275             continue;
1276         }
1277 
1278         frameStateBuilder_.AdvanceToNextBB(bb);
1279         if (IsEntryBlock(bb.id)) {
1280             if (NeedCheckSafePointAndStackOver()) {
1281                 GateRef state = frameStateBuilder_.GetCurrentState();
1282                 GateRef depend = frameStateBuilder_.GetCurrentDepend();
1283                 auto stackCheck = circuit_->NewGate(circuit_->CheckSafePointAndStackOver(), {state, depend});
1284                 bb.dependCache = stackCheck;
1285                 frameStateBuilder_.UpdateStateDepend(stackCheck, stackCheck);
1286             }
1287             auto *bbNext = &RegionAt(bb.id + 1);
1288             while (!IsEntryBlock(bbNext->id) && frameStateBuilder_.OutOfOsrLoop(*bbNext)) {
1289                 bbNext = &RegionAt(bbNext->id + 1);
1290             }
1291             frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
1292             bbNext->expandedPreds.push_back({bb.id, bb.end, false});
1293             continue;
1294         }
1295 
1296         HandleOsrLoopBody(bb);
1297     }
1298     for (size_t bbId : osrLoopExitBBIds) {
1299         auto &bb = RegionAt(bbId);
1300         GenDeoptAndReturnForOsrLoopExit(bb);
1301     }
1302 }
1303 
BuildCircuit()1304 void BytecodeCircuitBuilder::BuildCircuit()
1305 {
1306     if (IsOSR()) {
1307         // create osr arg gates array
1308         BuildOSRArgs();
1309         frameStateBuilder_.DoBytecodeAnalysis();
1310         if (TerminateAnalysis()) {
1311             return;
1312         }
1313         // build states sub-circuit of osr block
1314         BuildOsrCircuit();
1315     } else {
1316         // create arg gates array
1317         BuildCircuitArgs();
1318         frameStateBuilder_.DoBytecodeAnalysis();
1319         if (TerminateAnalysis()) {
1320             return;
1321         }
1322         // build states sub-circuit of each block
1323         BuildSubCircuit();
1324     }
1325     if (IsLogEnabled()) {
1326         PrintGraph(std::string("Bytecode2Gate [" + methodName_ + "]").c_str());
1327         LOG_COMPILER(INFO) << "\033[34m" << "============= "
1328                            << "After bytecode2circuit lowering ["
1329                            << methodName_ << "]"
1330                            << " =============" << "\033[0m";
1331         circuit_->PrintAllGatesWithBytecode();
1332         LOG_COMPILER(INFO) << "\033[34m" << "=========================== End ===========================" << "\033[0m";
1333     }
1334 }
1335 
PrintGraph(const char * title)1336 void BytecodeCircuitBuilder::PrintGraph(const char* title)
1337 {
1338     LOG_COMPILER(INFO) << "======================== " << title << " ========================";
1339     for (size_t i = 0; i < graph_.size(); i++) {
1340         BytecodeRegion& bb = RegionAt(i);
1341         if (!IsEntryBlock(bb.id) && bb.numOfStatePreds == 0) {
1342             LOG_COMPILER(INFO) << "B" << bb.id << ":                               ;preds= invalid BB";
1343             LOG_COMPILER(INFO) << "\tBytecodePC: [" << std::to_string(bb.start) << ", "
1344                                << std::to_string(bb.end) << ")";
1345             continue;
1346         }
1347         std::string log("B" + std::to_string(bb.id) + ":                               ;preds= ");
1348         for (size_t k = 0; k < bb.preds.size(); ++k) {
1349             log += std::to_string(bb.preds[k]->id) + ", ";
1350         }
1351         LOG_COMPILER(INFO) << log;
1352         if (IsEntryBlock(bb.id)) {
1353             LOG_COMPILER(INFO) << "\tBytecodePC: Empty";
1354         } else {
1355             LOG_COMPILER(INFO) << "\tBytecodePC: [" << std::to_string(bb.start) << ", "
1356                 << std::to_string(bb.end) << ")";
1357         }
1358 
1359         std::string log1("\tSucces: ");
1360         for (size_t j = 0; j < bb.succs.size(); j++) {
1361             log1 += std::to_string(bb.succs[j]->id) + ", ";
1362         }
1363         LOG_COMPILER(INFO) << log1;
1364 
1365         for (size_t j = 0; j < bb.catches.size(); j++) {
1366             LOG_COMPILER(INFO) << "\tcatch [: " << std::to_string(bb.catches[j]->start) << ", "
1367                                << std::to_string(bb.catches[j]->end) << ")";
1368         }
1369 
1370         std::string log2("\tTrys: ");
1371         for (auto tryBlock: bb.trys) {
1372             log2 += std::to_string(tryBlock->id) + " , ";
1373         }
1374         LOG_COMPILER(INFO) << log2;
1375 
1376         PrintBytecodeInfo(bb);
1377         LOG_COMPILER(INFO) << "";
1378     }
1379 }
1380 
PrintBytecodeInfo(BytecodeRegion & bb)1381 void BytecodeCircuitBuilder::PrintBytecodeInfo(BytecodeRegion& bb)
1382 {
1383     if (IsEntryBlock(bb.id)) {
1384         LOG_COMPILER(INFO) << "\tBytecode[] = Empty";
1385         return;
1386     }
1387     LOG_COMPILER(INFO) << "\tBytecode[] = ";
1388     EnumerateBlock(bb, [&](const BytecodeInfo &bytecodeInfo) -> bool {
1389         auto &iterator = bb.GetBytecodeIterator();
1390         std::string log;
1391         log += std::string("\t\t< ") + std::to_string(iterator.Index()) + ": ";
1392         log += GetEcmaOpcodeStr(iterator.GetBytecodeInfo().GetOpcode()) + ", " + "In=[";
1393         if (bytecodeInfo.AccIn()) {
1394             log += "acc,";
1395         }
1396         for (const auto &in: bytecodeInfo.inputs) {
1397             if (std::holds_alternative<VirtualRegister>(in)) {
1398                 log += std::to_string(std::get<VirtualRegister>(in).GetId()) + ",";
1399             }
1400         }
1401         log += "], Out=[";
1402         if (bytecodeInfo.AccOut()) {
1403             log += "acc,";
1404         }
1405         for (const auto &out: bytecodeInfo.vregOut) {
1406             log += std::to_string(out) + ",";
1407         }
1408         log += "] >";
1409         LOG_COMPILER(INFO) << log;
1410 
1411         auto gate = GetGateByBcIndex(iterator.Index());
1412         if (gate != Circuit::NullGate()) {
1413             this->gateAcc_.ShortPrint(gate);
1414         }
1415         return true;
1416     });
1417 }
1418 }  // namespace panda::ecmascript::kungfu
1419