• 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     GateRef state = frameStateBuilder_.GetCurrentState();
793     GateRef depend = frameStateBuilder_.GetCurrentDepend();
794     std::string comment = Deoptimizier::DisplayItems(DeoptType::INSUFFICIENTPROFILE);
795     GateRef type = circuit_->GetConstantGate(MachineType::I64, static_cast<int64_t>(DeoptType::INSUFFICIENTPROFILE),
796                                              GateType::NJSValue());
797     GateRef condition = circuit_->GetConstantGate(MachineType::I1, 0, GateType::NJSValue());
798     GateRef deopt = circuit_->NewGate(circuit_->DeoptCheck(), MachineType::I1,
799                                       {state, depend, condition, gateAcc_.FindNearestFrameState(depend), type},
800                                       GateType::NJSValue(), comment.c_str());
801     GateRef dependRelay = circuit_->NewGate(circuit_->DependRelay(), {deopt, depend});
802     GateRef undef =
803         circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_UNDEFINED, GateType::TaggedValue());
804     circuit_->NewGate(circuit_->Return(), {state, dependRelay, undef, circuit_->GetReturnRoot()});
805     byteCodeToJSGates_[iterator.Index()].emplace_back(deopt);
806     jsGatesToByteCode_[deopt] = iterator.Index();
807     return deopt;
808 }
809 
CreateGateInList(const BytecodeInfo & info,const GateMetaData * meta)810 std::vector<GateRef> BytecodeCircuitBuilder::CreateGateInList(
811     const BytecodeInfo &info, const GateMetaData *meta)
812 {
813     auto numValues = meta->GetNumIns();
814     const size_t length = meta->GetInValueStarts();
815     std::vector<GateRef> inList(numValues, Circuit::NullGate());
816     auto inputSize = info.inputs.size();
817     for (size_t i = 0; i < inputSize; i++) {
818         auto &input = info.inputs[i];
819         if (std::holds_alternative<ConstDataId>(input)) {
820             inList[i + length] = circuit_->GetConstantGate(MachineType::I64,
821                                                            std::get<ConstDataId>(input).GetId(),
822                                                            GateType::NJSValue());
823         } else if (std::holds_alternative<Immediate>(input)) {
824             inList[i + length] = circuit_->GetConstantGate(MachineType::I64,
825                                                            std::get<Immediate>(input).GetValue(),
826                                                            GateType::NJSValue());
827         } else if (std::holds_alternative<ICSlotId>(input)) {
828             inList[i + length] = circuit_->GetConstantGate(MachineType::I16,
829                                                            std::get<ICSlotId>(input).GetId(),
830                                                            GateType::NJSValue());
831         } else {
832             ASSERT(std::holds_alternative<VirtualRegister>(input));
833             continue;
834         }
835     }
836     if (info.AccIn()) {
837         inputSize++;
838     }
839     if (meta->HasFrameState()) {
840         inList[inputSize + length] = GetFrameArgs();
841     }
842     return inList;
843 }
844 
NewConst(const BytecodeInfo & info)845 GateRef BytecodeCircuitBuilder::NewConst(const BytecodeInfo &info)
846 {
847     auto opcode = info.GetOpcode();
848     GateRef gate = 0;
849     switch (opcode) {
850         case EcmaOpcode::LDNAN:
851             gate = circuit_->GetConstantGate(MachineType::I64,
852                                              base::NumberHelper::GetNaN(),
853                                              GateType::NumberType());
854             break;
855         case EcmaOpcode::LDINFINITY:
856             gate = circuit_->GetConstantGate(MachineType::I64,
857                                              base::NumberHelper::GetPositiveInfinity(),
858                                              GateType::NumberType());
859             break;
860         case EcmaOpcode::LDUNDEFINED:
861             gate = circuit_->GetConstantGate(MachineType::I64,
862                                              JSTaggedValue::VALUE_UNDEFINED,
863                                              GateType::UndefinedType());
864             break;
865         case EcmaOpcode::LDNULL:
866             gate = circuit_->GetConstantGate(MachineType::I64,
867                                              JSTaggedValue::VALUE_NULL,
868                                              GateType::NullType());
869             break;
870         case EcmaOpcode::LDTRUE:
871             gate = circuit_->GetConstantGate(MachineType::I64,
872                                              JSTaggedValue::VALUE_TRUE,
873                                              GateType::BooleanType());
874             break;
875         case EcmaOpcode::LDFALSE:
876             gate = circuit_->GetConstantGate(MachineType::I64,
877                                              JSTaggedValue::VALUE_FALSE,
878                                              GateType::BooleanType());
879             break;
880         case EcmaOpcode::LDHOLE:
881             gate = circuit_->GetConstantGate(MachineType::I64,
882                                              JSTaggedValue::VALUE_HOLE,
883                                              GateType::TaggedValue());
884             break;
885         case EcmaOpcode::LDAI_IMM32:
886             gate = circuit_->GetConstantGate(MachineType::I64,
887                                              std::get<Immediate>(info.inputs[0]).ToJSTaggedValueInt(),
888                                              GateType::IntType());
889             break;
890         case EcmaOpcode::FLDAI_IMM64:
891             gate = circuit_->GetConstantGate(MachineType::I64,
892                                              std::get<Immediate>(info.inputs.at(0)).ToJSTaggedValueDouble(),
893                                              GateType::DoubleType());
894             break;
895         case EcmaOpcode::LDFUNCTION:
896             gate = argAcc_.GetCommonArgGate(CommonArgIdx::FUNC);
897             break;
898         case EcmaOpcode::LDNEWTARGET:
899             gate = argAcc_.GetCommonArgGate(CommonArgIdx::NEW_TARGET);
900             break;
901         case EcmaOpcode::LDTHIS:
902             gate = argAcc_.GetCommonArgGate(CommonArgIdx::THIS_OBJECT);
903             break;
904         default:
905             LOG_ECMA(FATAL) << "this branch is unreachable";
906             UNREACHABLE();
907     }
908     return gate;
909 }
910 
MergeThrowGate(BytecodeRegion & bb,uint32_t bcIndex)911 void BytecodeCircuitBuilder::MergeThrowGate(BytecodeRegion &bb, uint32_t bcIndex)
912 {
913     auto state = frameStateBuilder_.GetCurrentState();
914     auto depend = frameStateBuilder_.GetCurrentDepend();
915     if (!bb.catches.empty()) {
916         auto ifSuccess = circuit_->NewGate(circuit_->IfSuccess(), {state});
917         auto dependRelay = circuit_->NewGate(circuit_->DependRelay(), {ifSuccess, depend});
918         auto ifException = circuit_->NewGate(circuit_->IfException(), {state, depend});
919         frameStateBuilder_.UpdateStateDepend(ifException, ifException);
920         ASSERT(bb.catches.size() == 1); // 1: one catch
921         auto bbNext = bb.catches.at(0);
922         frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
923         bbNext->expandedPreds.push_back({bb.id, bcIndex, true});
924         state = ifSuccess;
925         depend = dependRelay;
926     }
927     auto constant = circuit_->GetConstantGate(MachineType::I64,
928                                               JSTaggedValue::VALUE_EXCEPTION,
929                                               GateType::TaggedValue());
930     circuit_->NewGate(circuit_->Return(),
931         { state, depend, constant, circuit_->GetReturnRoot() });
932 }
933 
MergeExceptionGete(BytecodeRegion & bb,const BytecodeInfo & bytecodeInfo,uint32_t bcIndex)934 void BytecodeCircuitBuilder::MergeExceptionGete(BytecodeRegion &bb,
935     const BytecodeInfo& bytecodeInfo, uint32_t bcIndex)
936 {
937     auto state = frameStateBuilder_.GetCurrentState();
938     auto depend = frameStateBuilder_.GetCurrentDepend();
939     auto ifSuccess = circuit_->NewGate(circuit_->IfSuccess(), {state});
940     auto dependRelay = circuit_->NewGate(circuit_->DependRelay(), {ifSuccess, depend});
941     ASSERT(bb.catches.size() == 1); // 1: one catch
942     auto bbNext = bb.catches.at(0);
943     auto ifException = circuit_->NewGate(circuit_->IfException(), {state, depend});
944     frameStateBuilder_.UpdateStateDepend(ifException, ifException);
945     frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
946     if (bytecodeInfo.GetOpcode() == EcmaOpcode::CREATEASYNCGENERATOROBJ_V8) {
947         bbNext->expandedPreds.push_back({bb.id, bcIndex + 1, true}); // 1: next pc
948     } else {
949         bbNext->expandedPreds.push_back({bb.id, bcIndex, true});
950     }
951     depend = dependRelay;
952     frameStateBuilder_.UpdateStateDepend(ifSuccess, depend);
953 }
954 
NewJSGate(BytecodeRegion & bb)955 void BytecodeCircuitBuilder::NewJSGate(BytecodeRegion &bb)
956 {
957     auto &iterator = bb.GetBytecodeIterator();
958     const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo();
959     GateRef state = frameStateBuilder_.GetCurrentState();
960     GateRef depend = frameStateBuilder_.GetCurrentDepend();
961     size_t numValueInputs = bytecodeInfo.ComputeValueInputCount();
962     GateRef gate = 0;
963     bool writable = !bytecodeInfo.NoSideEffects();
964     bool hasFrameState = bytecodeInfo.HasFrameState();
965     size_t pcOffset = GetPcOffset(iterator.Index());
966     auto methodOffset = method_->GetMethodId().GetOffset();
967     auto meta = circuit_->JSBytecode(
968         numValueInputs, methodOffset, bytecodeInfo.GetOpcode(), pcOffset, iterator.Index(), writable, hasFrameState);
969     std::vector<GateRef> inList = CreateGateInList(bytecodeInfo, meta);
970     if (bytecodeInfo.IsDef()) {
971         gate = circuit_->NewGate(meta, MachineType::I64, inList.size(),
972                                  inList.data(), GateType::AnyType());
973     } else {
974         gate = circuit_->NewGate(meta, MachineType::NOVALUE, inList.size(),
975                                  inList.data(), GateType::Empty());
976     }
977     byteCodeToJSGates_[iterator.Index()].emplace_back(gate);
978     jsGatesToByteCode_[gate] = iterator.Index();
979     gateAcc_.NewIn(gate, 0, state);
980     gateAcc_.NewIn(gate, 1, depend);
981     frameStateBuilder_.UpdateStateDepend(gate, gate);
982     frameStateBuilder_.UpdateFrameValues(bytecodeInfo, iterator.Index(), gate);
983     if (bytecodeInfo.IsThrow()) {
984         MergeThrowGate(bb, iterator.Index());
985         return;
986     }
987 
988     if (!bb.catches.empty() && !bytecodeInfo.NoThrow()) {
989         MergeExceptionGete(bb, bytecodeInfo, iterator.Index());
990     } else if (!bb.catches.empty()) {
991         frameStateBuilder_.GetOrOCreateMergedContext(bb.catches.at(0)->id);
992     }
993     if (bytecodeInfo.IsGeneratorRelative()) {
994         suspendAndResumeGates_.emplace_back(gate);
995     }
996 }
997 
NewJump(BytecodeRegion & bb)998 void BytecodeCircuitBuilder::NewJump(BytecodeRegion &bb)
999 {
1000     auto &iterator = bb.GetBytecodeIterator();
1001     const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo();
1002     GateRef state = frameStateBuilder_.GetCurrentState();
1003     GateRef depend = frameStateBuilder_.GetCurrentDepend();
1004     size_t numValueInputs = bytecodeInfo.ComputeValueInputCount();
1005     if (bytecodeInfo.IsCondJump() && bb.succs.size() == 2) { // 2: two succ
1006         size_t pcOffset = GetPcOffset(iterator.Index());
1007         auto methodOffset = method_->GetMethodId().GetOffset();
1008         auto meta = circuit_->JSBytecode(
1009             numValueInputs, methodOffset, bytecodeInfo.GetOpcode(), pcOffset, iterator.Index(), false, false);
1010         auto numValues = meta->GetNumIns();
1011         GateRef gate = circuit_->NewGate(meta, std::vector<GateRef>(numValues, Circuit::NullGate()));
1012         gateAcc_.NewIn(gate, 0, state);
1013         gateAcc_.NewIn(gate, 1, depend);
1014         frameStateBuilder_.UpdateStateDepend(gate, gate);
1015         frameStateBuilder_.UpdateFrameValues(bytecodeInfo, iterator.Index(), gate);
1016 
1017         auto ifTrue = circuit_->NewGate(circuit_->IfTrue(), {gate});
1018         auto trueRelay = circuit_->NewGate(circuit_->DependRelay(), {ifTrue, gate});
1019         auto ifFalse = circuit_->NewGate(circuit_->IfFalse(), {gate});
1020         auto falseRelay = circuit_->NewGate(circuit_->DependRelay(), {ifFalse, gate});
1021         for (auto &bbNext: bb.succs) {
1022             if (iterator.Index() + 1 == bbNext->start) {
1023                 frameStateBuilder_.UpdateStateDepend(ifFalse, falseRelay);
1024                 frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
1025                 bbNext->expandedPreds.push_back({bb.id, iterator.Index(), false});
1026             } else {
1027                 frameStateBuilder_.UpdateStateDepend(ifTrue, trueRelay);
1028                 frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
1029                 bbNext->expandedPreds.push_back({bb.id, iterator.Index(), false});
1030             }
1031         }
1032         byteCodeToJSGates_[iterator.Index()].emplace_back(gate);
1033         jsGatesToByteCode_[gate] = iterator.Index();
1034     } else {
1035         ASSERT(bb.succs.size() == 1); // 1: only one succ if not condjump
1036         auto &bbNext = bb.succs.at(0);
1037         frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
1038         bbNext->expandedPreds.push_back({bb.id, iterator.Index(), false});
1039     }
1040 
1041     if (!bb.catches.empty()) {
1042         frameStateBuilder_.GetOrOCreateMergedContext(bb.catches.at(0)->id);
1043     }
1044 }
1045 
NewReturn(BytecodeRegion & bb)1046 GateRef BytecodeCircuitBuilder::NewReturn(BytecodeRegion &bb)
1047 {
1048     ASSERT(bb.succs.empty());
1049     auto &iterator = bb.GetBytecodeIterator();
1050     const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo();
1051     GateRef state = frameStateBuilder_.GetCurrentState();
1052     GateRef depend = frameStateBuilder_.GetCurrentDepend();
1053     GateRef gate = Circuit::NullGate();
1054     if (bytecodeInfo.GetOpcode() == EcmaOpcode::RETURN) {
1055         // handle return.dyn bytecode
1056         gate = circuit_->NewGate(circuit_->Return(),
1057             { state, depend, Circuit::NullGate(), circuit_->GetReturnRoot() });
1058         byteCodeToJSGates_[iterator.Index()].emplace_back(gate);
1059         jsGatesToByteCode_[gate] = iterator.Index();
1060     } else if (bytecodeInfo.GetOpcode() == EcmaOpcode::RETURNUNDEFINED) {
1061         // handle returnundefined bytecode
1062         auto constant = circuit_->GetConstantGate(MachineType::I64,
1063                                                   JSTaggedValue::VALUE_UNDEFINED,
1064                                                   GateType::TaggedValue());
1065         gate = circuit_->NewGate(circuit_->Return(),
1066             { state, depend, constant, circuit_->GetReturnRoot() });
1067         byteCodeToJSGates_[iterator.Index()].emplace_back(gate);
1068         jsGatesToByteCode_[gate] = iterator.Index();
1069     }
1070     return gate;
1071 }
1072 
NewByteCode(BytecodeRegion & bb)1073 void BytecodeCircuitBuilder::NewByteCode(BytecodeRegion &bb)
1074 {
1075     auto &iterator = bb.GetBytecodeIterator();
1076     const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo();
1077     FrameLiveOut* liveout;
1078     auto bcId = iterator.Index();
1079     if (bcId != 0 && iterator.IsInRange(bcId - 1)) {
1080         liveout = frameStateBuilder_.GetOrOCreateBCEndLiveOut(bcId - 1);
1081     } else {
1082         liveout = frameStateBuilder_.GetOrOCreateBBLiveOut(bb.id);
1083     }
1084     frameStateBuilder_.AdvanceToNextBc(bytecodeInfo, liveout, bcId);
1085     GateRef gate = Circuit::NullGate();
1086     if (bytecodeInfo.IsInsufficientProfile()) {
1087         // handle general ecma.* bytecodes
1088         NewDeopt(bb);
1089     } else if (bytecodeInfo.IsSetConstant()) {
1090         // handle bytecode command to get constants
1091         gate = NewConst(bytecodeInfo);
1092         byteCodeToJSGates_[iterator.Index()].emplace_back(gate);
1093         jsGatesToByteCode_[gate] = iterator.Index();
1094     } else if (bytecodeInfo.IsGeneral()) {
1095         // handle general ecma.* bytecodes
1096         NewJSGate(bb);
1097     } else if (bytecodeInfo.IsJump()) {
1098         // handle conditional jump and unconditional jump bytecodes
1099         NewJump(bb);
1100     } else if (bytecodeInfo.IsReturn()) {
1101         // handle return.dyn and returnundefined bytecodes
1102         gate = NewReturn(bb);
1103     } else if (bytecodeInfo.IsMov()) {
1104         frameStateBuilder_.UpdateMoveValues(bytecodeInfo);
1105     } else if (!bytecodeInfo.IsDiscarded()) {
1106         LOG_ECMA(FATAL) << "this branch is unreachable";
1107         UNREACHABLE();
1108     }
1109     if (gate != Circuit::NullGate()) {
1110         frameStateBuilder_.UpdateFrameValues(bytecodeInfo, iterator.Index(), gate);
1111     }
1112 }
1113 
BuildSubCircuit()1114 void BytecodeCircuitBuilder::BuildSubCircuit()
1115 {
1116     auto &entryBlock = RegionAt(0);
1117     frameStateBuilder_.InitEntryBB(entryBlock);
1118     auto& rpoList = frameStateBuilder_.GetRpoList();
1119     for (auto &bbId: rpoList) {
1120         auto &bb = RegionAt(bbId);
1121         frameStateBuilder_.AdvanceToNextBB(bb);
1122         if (IsEntryBlock(bb.id)) {
1123             if (NeedCheckSafePointAndStackOver()) {
1124                 GateRef state = frameStateBuilder_.GetCurrentState();
1125                 GateRef depend = frameStateBuilder_.GetCurrentDepend();
1126                 auto stackCheck = circuit_->NewGate(circuit_->CheckSafePointAndStackOver(), {state, depend});
1127                 bb.dependCache = stackCheck;
1128                 frameStateBuilder_.UpdateStateDepend(stackCheck, stackCheck);
1129             }
1130             auto &bbNext = RegionAt(bb.id + 1);
1131             frameStateBuilder_.MergeIntoSuccessor(bb, bbNext);
1132             bbNext.expandedPreds.push_back({bb.id, bb.end, false});
1133             continue;
1134         }
1135         if (!bb.trys.empty()) {
1136             GateRef state = frameStateBuilder_.GetCurrentState();
1137             GateRef depend = frameStateBuilder_.GetCurrentDepend();
1138             auto getException = circuit_->NewGate(circuit_->GetException(),
1139                 MachineType::I64, {state, depend}, GateType::AnyType());
1140             frameStateBuilder_.UpdateAccumulator(getException);
1141             frameStateBuilder_.UpdateStateDepend(state, getException);
1142         }
1143         EnumerateBlock(bb, [this, &bb]
1144             (const BytecodeInfo &bytecodeInfo) -> bool {
1145             NewByteCode(bb);
1146             if (bytecodeInfo.IsJump() || bytecodeInfo.IsThrow()) {
1147                 return false;
1148             }
1149             return true;
1150         });
1151         bool needFallThrough = true;
1152         if (!bb.IsEmptryBlock()) {
1153             const BytecodeInfo& bytecodeInfo = GetBytecodeInfo(bb.end);
1154             needFallThrough = bytecodeInfo.needFallThrough();
1155         }
1156         // fallThrough or empty merge bb
1157         if (needFallThrough) {
1158             ASSERT(bb.succs.size() == 1); // 1: fall through
1159             auto &bbNext = RegionAt(bb.succs[0]->id);
1160             frameStateBuilder_.MergeIntoSuccessor(bb, bbNext);
1161             bbNext.expandedPreds.push_back({bb.id, bb.end, false});
1162         }
1163     }
1164 }
1165 
FindOsrLoopHeadBB()1166 bool BytecodeCircuitBuilder::FindOsrLoopHeadBB()
1167 {
1168     int32_t loopBackBcIndex {-1};
1169     for (size_t k = 0; k < pcOffsets_.size(); k++) {
1170         if (static_cast<int32_t>(pcOffsets_[k] - pcOffsets_[0]) == osrOffset_) {
1171             loopBackBcIndex = static_cast<int32_t>(k);
1172         }
1173     }
1174     if (loopBackBcIndex == -1) {
1175         LOG_COMPILER(ERROR) << "Unable to find the loop back of OSR.";
1176         return false;
1177     }
1178     auto &rpoList = frameStateBuilder_.GetRpoList();
1179     for (auto &bbId : rpoList) {
1180         auto &bb = RegionAt(bbId);
1181         if (bb.end == static_cast<uint32_t>(loopBackBcIndex)) {
1182             frameStateBuilder_.SetOsrLoopHeadBB(bb);
1183             return true;
1184         }
1185     }
1186     LOG_COMPILER(ERROR) << "Unable to find the loop head bb of OSR.";
1187     return false;
1188 }
1189 
GenDeoptAndReturnForOsrLoopExit(BytecodeRegion & osrLoopExitBB)1190 void BytecodeCircuitBuilder::GenDeoptAndReturnForOsrLoopExit(BytecodeRegion &osrLoopExitBB)
1191 {
1192     frameStateBuilder_.AdvanceToNextBB(osrLoopExitBB, true);
1193     GateRef state = frameStateBuilder_.GetCurrentState();
1194     GateRef depend = frameStateBuilder_.GetCurrentDepend();
1195     std::string comment = Deoptimizier::DisplayItems(DeoptType::OSRLOOPEXIT);
1196     GateRef type =
1197         circuit_->GetConstantGate(MachineType::I64, static_cast<int64_t>(DeoptType::OSRLOOPEXIT), GateType::NJSValue());
1198     GateRef condition = circuit_->GetConstantGate(MachineType::I1, 0, GateType::NJSValue());
1199     GateRef deopt = circuit_->NewGate(circuit_->DeoptCheck(), MachineType::I1,
1200                                       {state, depend, condition, gateAcc_.GetFrameState(depend), type},
1201                                       GateType::NJSValue(), comment.c_str());
1202     GateRef dependRelay = circuit_->NewGate(circuit_->DependRelay(), {deopt, depend});
1203     GateRef undef =
1204         circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_UNDEFINED, GateType::TaggedValue());
1205     circuit_->NewGate(circuit_->Return(), {state, dependRelay, undef, circuit_->GetReturnRoot()});
1206 }
1207 
CollectCacheBBforOSRLoop(BytecodeRegion * bb)1208 void BytecodeCircuitBuilder::CollectCacheBBforOSRLoop(BytecodeRegion *bb)
1209 {
1210     catchBBOfOSRLoop_.insert(bb);
1211     for (BytecodeRegion *succBB : bb->succs) {
1212         CollectCacheBBforOSRLoop(succBB);
1213     }
1214 }
1215 
HandleOsrLoopBody(BytecodeRegion & osrLoopBodyBB)1216 void BytecodeCircuitBuilder::HandleOsrLoopBody(BytecodeRegion &osrLoopBodyBB)
1217 {
1218     if (!osrLoopBodyBB.trys.empty()) {
1219         GateRef state = frameStateBuilder_.GetCurrentState();
1220         GateRef depend = frameStateBuilder_.GetCurrentDepend();
1221         auto getException =
1222             circuit_->NewGate(circuit_->GetException(), MachineType::I64, {state, depend}, GateType::AnyType());
1223         frameStateBuilder_.UpdateAccumulator(getException);
1224         frameStateBuilder_.UpdateStateDepend(state, getException);
1225     }
1226     // collect catch BB.
1227     if (!osrLoopBodyBB.catches.empty()) {
1228         for (BytecodeRegion *targetBB : osrLoopBodyBB.catches) {
1229             CollectCacheBBforOSRLoop(targetBB);
1230         }
1231     }
1232     EnumerateBlock(osrLoopBodyBB, [this, &osrLoopBodyBB](const BytecodeInfo &bytecodeInfo) -> bool {
1233         NewByteCode(osrLoopBodyBB);
1234         if (bytecodeInfo.IsJump() || bytecodeInfo.IsThrow()) {
1235             return false;
1236         }
1237         return true;
1238     });
1239     bool needFallThrough = true;
1240     if (!osrLoopBodyBB.IsEmptryBlock()) {
1241         const BytecodeInfo &bytecodeInfo = GetBytecodeInfo(osrLoopBodyBB.end);
1242         needFallThrough = bytecodeInfo.needFallThrough();
1243     }
1244     // fallThrough or empty merge osrLoopBodyBB
1245     if (needFallThrough) {
1246         ASSERT(osrLoopBodyBB.succs.size() == 1);  // 1: fall through
1247         auto &bbNext = RegionAt(osrLoopBodyBB.succs[0]->id);
1248         frameStateBuilder_.MergeIntoSuccessor(osrLoopBodyBB, bbNext);
1249         bbNext.expandedPreds.push_back({osrLoopBodyBB.id, osrLoopBodyBB.end, false});
1250     }
1251 }
1252 
BuildOsrCircuit()1253 void BytecodeCircuitBuilder::BuildOsrCircuit()
1254 {
1255     if (!FindOsrLoopHeadBB()) {
1256         LOG_COMPILER(FATAL) << "invalid osr offset";
1257     }
1258     circuit_->SetIsOsr();
1259     auto &entryBlock = RegionAt(0);
1260     frameStateBuilder_.InitEntryBB(entryBlock);
1261     std::set<size_t> osrLoopExitBBIds;
1262     auto &rpoList = frameStateBuilder_.GetRpoList();
1263     for (auto &bbId : rpoList) {
1264         auto &bb = RegionAt(bbId);
1265         if (frameStateBuilder_.IsOsrLoopExit(bb)) {
1266             // The loop exit BB is in front of the loop head BB. At this time,
1267             // the loop exit BB does not have the context object, and the processing of the loop exit BB is delayed.
1268             if (frameStateBuilder_.IsContextExists(bb.id)) {
1269                 GenDeoptAndReturnForOsrLoopExit(bb);
1270             } else {
1271                 osrLoopExitBBIds.insert(bbId);
1272             }
1273             continue;
1274         }
1275 
1276         // Processes only the BBs related to the loop specified by the OSR.
1277         if (!IsEntryBlock(bb.id) && frameStateBuilder_.OutOfOsrLoop(bb) && !IsCacheBBOfOSRLoop(bb)) {
1278             continue;
1279         }
1280 
1281         frameStateBuilder_.AdvanceToNextBB(bb);
1282         if (IsEntryBlock(bb.id)) {
1283             if (NeedCheckSafePointAndStackOver()) {
1284                 GateRef state = frameStateBuilder_.GetCurrentState();
1285                 GateRef depend = frameStateBuilder_.GetCurrentDepend();
1286                 auto stackCheck = circuit_->NewGate(circuit_->CheckSafePointAndStackOver(), {state, depend});
1287                 bb.dependCache = stackCheck;
1288                 frameStateBuilder_.UpdateStateDepend(stackCheck, stackCheck);
1289             }
1290             auto *bbNext = &RegionAt(bb.id + 1);
1291             while (!IsEntryBlock(bbNext->id) && frameStateBuilder_.OutOfOsrLoop(*bbNext)) {
1292                 bbNext = &RegionAt(bbNext->id + 1);
1293             }
1294             frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext);
1295             bbNext->expandedPreds.push_back({bb.id, bb.end, false});
1296             continue;
1297         }
1298 
1299         HandleOsrLoopBody(bb);
1300     }
1301     for (size_t bbId : osrLoopExitBBIds) {
1302         auto &bb = RegionAt(bbId);
1303         GenDeoptAndReturnForOsrLoopExit(bb);
1304     }
1305 }
1306 
BuildCircuit()1307 void BytecodeCircuitBuilder::BuildCircuit()
1308 {
1309     if (IsOSR()) {
1310         // create osr arg gates array
1311         BuildOSRArgs();
1312         frameStateBuilder_.DoBytecodeAnalysis();
1313         if (TerminateAnalysis()) {
1314             return;
1315         }
1316         // build states sub-circuit of osr block
1317         BuildOsrCircuit();
1318     } else {
1319         // create arg gates array
1320         BuildCircuitArgs();
1321         frameStateBuilder_.DoBytecodeAnalysis();
1322         if (TerminateAnalysis()) {
1323             return;
1324         }
1325         // build states sub-circuit of each block
1326         BuildSubCircuit();
1327     }
1328     if (IsLogEnabled()) {
1329         PrintGraph(std::string("Bytecode2Gate [" + methodName_ + "]").c_str());
1330         LOG_COMPILER(INFO) << "\033[34m" << "============= "
1331                            << "After bytecode2circuit lowering ["
1332                            << methodName_ << "]"
1333                            << " =============" << "\033[0m";
1334         circuit_->PrintAllGatesWithBytecode();
1335         LOG_COMPILER(INFO) << "\033[34m" << "=========================== End ===========================" << "\033[0m";
1336     }
1337 }
1338 
PrintGraph(const char * title)1339 void BytecodeCircuitBuilder::PrintGraph(const char* title)
1340 {
1341     LOG_COMPILER(INFO) << "======================== " << title << " ========================";
1342     for (size_t i = 0; i < graph_.size(); i++) {
1343         BytecodeRegion& bb = RegionAt(i);
1344         if (!IsEntryBlock(bb.id) && bb.numOfStatePreds == 0) {
1345             LOG_COMPILER(INFO) << "B" << bb.id << ":                               ;preds= invalid BB";
1346             LOG_COMPILER(INFO) << "\tBytecodePC: [" << std::to_string(bb.start) << ", "
1347                                << std::to_string(bb.end) << ")";
1348             continue;
1349         }
1350         std::string log("B" + std::to_string(bb.id) + ":                               ;preds= ");
1351         for (size_t k = 0; k < bb.preds.size(); ++k) {
1352             log += std::to_string(bb.preds[k]->id) + ", ";
1353         }
1354         LOG_COMPILER(INFO) << log;
1355         if (IsEntryBlock(bb.id)) {
1356             LOG_COMPILER(INFO) << "\tBytecodePC: Empty";
1357         } else {
1358             LOG_COMPILER(INFO) << "\tBytecodePC: [" << std::to_string(bb.start) << ", "
1359                 << std::to_string(bb.end) << ")";
1360         }
1361 
1362         std::string log1("\tSucces: ");
1363         for (size_t j = 0; j < bb.succs.size(); j++) {
1364             log1 += std::to_string(bb.succs[j]->id) + ", ";
1365         }
1366         LOG_COMPILER(INFO) << log1;
1367 
1368         for (size_t j = 0; j < bb.catches.size(); j++) {
1369             LOG_COMPILER(INFO) << "\tcatch [: " << std::to_string(bb.catches[j]->start) << ", "
1370                                << std::to_string(bb.catches[j]->end) << ")";
1371         }
1372 
1373         std::string log2("\tTrys: ");
1374         for (auto tryBlock: bb.trys) {
1375             log2 += std::to_string(tryBlock->id) + " , ";
1376         }
1377         LOG_COMPILER(INFO) << log2;
1378 
1379         PrintBytecodeInfo(bb);
1380         LOG_COMPILER(INFO) << "";
1381     }
1382 }
1383 
PrintBytecodeInfo(BytecodeRegion & bb)1384 void BytecodeCircuitBuilder::PrintBytecodeInfo(BytecodeRegion& bb)
1385 {
1386     if (IsEntryBlock(bb.id)) {
1387         LOG_COMPILER(INFO) << "\tBytecode[] = Empty";
1388         return;
1389     }
1390     LOG_COMPILER(INFO) << "\tBytecode[] = ";
1391     EnumerateBlock(bb, [&](const BytecodeInfo &bytecodeInfo) -> bool {
1392         auto &iterator = bb.GetBytecodeIterator();
1393         std::string log;
1394         log += std::string("\t\t< ") + std::to_string(iterator.Index()) + ": ";
1395         log += GetEcmaOpcodeStr(iterator.GetBytecodeInfo().GetOpcode()) + ", " + "In=[";
1396         if (bytecodeInfo.AccIn()) {
1397             log += "acc,";
1398         }
1399         for (const auto &in: bytecodeInfo.inputs) {
1400             if (std::holds_alternative<VirtualRegister>(in)) {
1401                 log += std::to_string(std::get<VirtualRegister>(in).GetId()) + ",";
1402             }
1403         }
1404         log += "], Out=[";
1405         if (bytecodeInfo.AccOut()) {
1406             log += "acc,";
1407         }
1408         for (const auto &out: bytecodeInfo.vregOut) {
1409             log += std::to_string(out) + ",";
1410         }
1411         log += "] >";
1412         LOG_COMPILER(INFO) << log;
1413 
1414         auto gate = GetGateByBcIndex(iterator.Index());
1415         if (gate != Circuit::NullGate()) {
1416             this->gateAcc_.ShortPrint(gate);
1417         }
1418         return true;
1419     });
1420 }
1421 }  // namespace panda::ecmascript::kungfu
1422