• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 #ifndef ECMASCRIPT_CLASS_LINKER_BYTECODE_CIRCUIT_IR_BUILDER_H
17 #define ECMASCRIPT_CLASS_LINKER_BYTECODE_CIRCUIT_IR_BUILDER_H
18 
19 #include <algorithm>
20 #include <numeric>
21 #include <tuple>
22 #include <utility>
23 #include <vector>
24 #include <variant>
25 
26 #include "ecmascript/compiler/argument_accessor.h"
27 #include "ecmascript/compiler/bytecode_info_collector.h"
28 #include "ecmascript/compiler/bytecodes.h"
29 #include "ecmascript/compiler/circuit.h"
30 #include "ecmascript/compiler/compiler_log.h"
31 #include "ecmascript/compiler/ecma_opcode_des.h"
32 #include "ecmascript/compiler/frame_states.h"
33 #include "ecmascript/compiler/loop_analysis.h"
34 #include "ecmascript/compiler/pgo_type/pgo_type_recorder.h"
35 #include "ecmascript/compiler/type_recorder.h"
36 #include "ecmascript/interpreter/interpreter-inl.h"
37 #include "ecmascript/jspandafile/js_pandafile.h"
38 #include "ecmascript/jspandafile/method_literal.h"
39 #include "ecmascript/compiler/compiler_log.h"
40 #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
41 
42 namespace panda::ecmascript::kungfu {
43 struct ExceptionItem {
44     uint8_t* startPc;
45     uint8_t* endPc;
46     std::vector<uint8_t*> catches;
47 
ExceptionItemExceptionItem48     ExceptionItem(uint8_t* startPc, uint8_t* endPc, std::vector<uint8_t*> catches)
49         : startPc(startPc), endPc(endPc), catches(catches) {}
50 };
51 
52 using ExceptionInfo = std::vector<ExceptionItem>;
53 
54 class RegionItem {
55 public:
56     static constexpr uint32_t INVALID_BC_INDEX = static_cast<uint32_t>(-1);
57     bool operator<(const RegionItem &rhs) const
58     {
59         return this->startBcIndex_ < rhs.startBcIndex_;
60     }
61 
62     bool operator>(const RegionItem &rhs) const
63     {
64         return this->startBcIndex_ > rhs.startBcIndex_;
65     }
66 
67     bool operator==(const RegionItem &rhs) const
68     {
69         return this->startBcIndex_ == rhs.startBcIndex_;
70     }
71 
RegionItem(uint32_t startBcIndex,bool isHeadBlock)72     RegionItem(uint32_t startBcIndex, bool isHeadBlock)
73         : startBcIndex_(startBcIndex), isHeadBlock_(isHeadBlock) {}
74 
GetStartBcIndex()75     uint32_t GetStartBcIndex() const
76     {
77         return startBcIndex_;
78     }
79 
IsHeadBlock()80     uint32_t IsHeadBlock() const
81     {
82         return isHeadBlock_;
83     }
84 private:
85     uint32_t startBcIndex_ { INVALID_BC_INDEX };
86     bool isHeadBlock_ { false };
87     friend class RegionsInfo;
88 };
89 
90 struct BytecodeSplitItem {
BytecodeSplitItemBytecodeSplitItem91     BytecodeSplitItem(uint32_t start, uint32_t pred)
92         : startBcIndex(start), predBcIndex(pred) {}
93     uint32_t startBcIndex { RegionItem::INVALID_BC_INDEX };
94     uint32_t predBcIndex { RegionItem::INVALID_BC_INDEX };
95 };
96 
97 class RegionsInfo {
98 public:
InsertJump(uint32_t bcIndex,uint32_t predBcIndex,bool isJumpImm)99     void InsertJump(uint32_t bcIndex, uint32_t predBcIndex, bool isJumpImm)
100     {
101         InsertBlockItem(bcIndex, false);
102         auto fallThrogth = bcIndex - 1; // 1: fall through
103         // isJumpImm will not generate fall through
104         if (isJumpImm || fallThrogth != predBcIndex) {
105             InsertSplitItem(bcIndex, predBcIndex);
106         }
107     }
108 
InsertHead(uint32_t bcIndex)109     void InsertHead(uint32_t bcIndex)
110     {
111         InsertBlockItem(bcIndex, true);
112     }
113 
InsertSplit(uint32_t bcIndex)114     void InsertSplit(uint32_t bcIndex)
115     {
116         InsertBlockItem(bcIndex, false);
117     }
118 
FindBBIndexByBcIndex(uint32_t bcIndex)119     size_t FindBBIndexByBcIndex(uint32_t bcIndex) const
120     {
121         auto findFunc = [] (uint32_t value, const RegionItem &item) {
122             return value < item.startBcIndex_;
123         };
124         const auto &it = std::upper_bound(blockItems_.begin(),
125             blockItems_.end(), bcIndex, findFunc);
126         if (it == blockItems_.end()) {
127             return blockItems_.size();
128         }
129         // blockItems_[0]'s value is 0, bcIndex must be: bcIndex > blockItems_.begin()
130         return std::distance(blockItems_.begin(), it);
131     }
132 
GetSplitItems()133     const std::vector<BytecodeSplitItem> &GetSplitItems() const
134     {
135         return splitItems_;
136     }
137 
GetBlockItems()138     const std::set<RegionItem> &GetBlockItems() const
139     {
140         return blockItems_;
141     }
142 private:
InsertBlockItem(uint32_t bcIndex,bool isHeadBlock)143     void InsertBlockItem(uint32_t bcIndex, bool isHeadBlock)
144     {
145         auto result = blockItems_.insert(RegionItem { bcIndex, isHeadBlock });
146         if (!result.second && isHeadBlock) {
147             blockItems_.erase(result.first);
148             blockItems_.insert(RegionItem { bcIndex, isHeadBlock });
149         }
150     }
151 
InsertSplitItem(uint32_t bcIndex,uint32_t predBcIndex)152     void InsertSplitItem(uint32_t bcIndex, uint32_t predBcIndex)
153     {
154         splitItems_.emplace_back(BytecodeSplitItem { bcIndex, predBcIndex });
155     }
156     std::set<RegionItem> blockItems_ {};
157     std::vector<BytecodeSplitItem> splitItems_ {};
158 };
159 
160 struct BytecodeRegion {
161     size_t id {0};
162     uint32_t start {0};
163     uint32_t end {0};
164     ChunkVector<BytecodeRegion *> preds; // List of predessesor blocks
165     ChunkVector<BytecodeRegion *> succs; // List of successors blocks
166     ChunkVector<BytecodeRegion *> trys; // List of trys blocks
167     ChunkVector<BytecodeRegion *> catches; // List of catches blocks
168     size_t numOfStatePreds {0};
169     size_t loopNumber {0};
170     size_t loopIndex {0};
171     ChunkVector<std::tuple<size_t, size_t, bool>> expandedPreds;
172     GateRef dependCache {Circuit::NullGate()};
173     BytecodeIterator bytecodeIterator_ {};
174 
BytecodeRegionBytecodeRegion175     BytecodeRegion(Chunk* chunk) : preds(chunk), succs(chunk),
176         trys(chunk), catches(chunk), expandedPreds(chunk)
177     {
178     }
179 
GetBytecodeIteratorBytecodeRegion180     BytecodeIterator &GetBytecodeIterator() {
181         return bytecodeIterator_;
182     }
183 
184     bool operator <(const BytecodeRegion &target) const
185     {
186         return id < target.id;
187     }
188 
SortCatchesBytecodeRegion189     void SortCatches()
190     {
191         if (catches.size() > 1) {
192             std::sort(catches.begin(), catches.end(), [](BytecodeRegion *first, BytecodeRegion *second) {
193                 return first->start < second->start;
194             });
195         }
196     }
197 
EraseThisBlockBytecodeRegion198     void EraseThisBlock(ChunkVector<BytecodeRegion *> &blocks)
199     {
200         auto it = std::find(blocks.begin(), blocks.end(), this);
201         if (it != blocks.end()) {
202             blocks.erase(it);
203         }
204     }
205 
IsEmptryBlockBytecodeRegion206     bool IsEmptryBlock() const
207     {
208         return end == static_cast<uint32_t>(BytecodeIterator::INVALID_INDEX);
209     }
210 };
211 
212 using BytecodeGraph = ChunkVector<BytecodeRegion*>;
213 
214 class BytecodeCircuitBuilder {
215 public:
BytecodeCircuitBuilder(const JSPandaFile * jsPandaFile,const MethodLiteral * methodLiteral,const MethodPcInfo & methodPCInfo,TSManager * tsManager,Circuit * circuit,Bytecodes * bytecodes,bool hasTypes,bool enableLog,bool enableTypeLowering,std::string name,const CString & recordName,PGOProfilerDecoder * decoder,bool isInline,bool enableOptTrackField)216     BytecodeCircuitBuilder(const JSPandaFile *jsPandaFile,
217                            const MethodLiteral *methodLiteral,
218                            const MethodPcInfo &methodPCInfo,
219                            TSManager *tsManager,
220                            Circuit *circuit,
221                            Bytecodes *bytecodes,
222                            bool hasTypes,
223                            bool enableLog,
224                            bool enableTypeLowering,
225                            std::string name,
226                            const CString &recordName,
227                            PGOProfilerDecoder *decoder,
228                            bool isInline,
229                            bool enableOptTrackField)
230         : tsManager_(tsManager), circuit_(circuit), graph_(circuit->chunk()), file_(jsPandaFile),
231           method_(methodLiteral), gateAcc_(circuit), argAcc_(circuit, method_),
232           typeRecorder_(jsPandaFile, method_, tsManager, recordName, decoder, methodPCInfo, bytecodes,
233             enableOptTrackField),
234           pgoTypeRecorder_(*decoder, jsPandaFile, method_->GetMethodId().GetOffset()),
235           hasTypes_(hasTypes), enableLog_(enableLog), enableTypeLowering_(enableTypeLowering),
236           pcOffsets_(methodPCInfo.pcOffsets),
237           frameStateBuilder_(this, circuit, methodLiteral),
238           methodName_(name), recordName_(recordName),
239           bytecodes_(bytecodes),
240           loopHeaderGates_(circuit->chunk()),
241           preFrameState_(circuit_->GetRoot()),
242           preFrameArgs_(circuit_->GetRoot()),
243           isInline_(isInline)
244     {
245     }
246     ~BytecodeCircuitBuilder() = default;
247     NO_COPY_SEMANTIC(BytecodeCircuitBuilder);
248     NO_MOVE_SEMANTIC(BytecodeCircuitBuilder);
249     void PUBLIC_API BytecodeToCircuit();
250     void CollectRegionInfo(uint32_t bcIndex);
251 
GetCircuit()252     [[nodiscard]] Circuit* GetCircuit() const
253     {
254         return circuit_;
255     }
256 
GetGateByBcIndex(uint32_t bcIndex)257     GateRef GetGateByBcIndex(uint32_t bcIndex) const
258     {
259         ASSERT(bcIndex < byteCodeToJSGates_.size());
260         if (byteCodeToJSGates_[bcIndex].size() > 0) {
261             ASSERT(byteCodeToJSGates_[bcIndex].size() == 1);
262             return byteCodeToJSGates_[bcIndex].at(0);
263         }
264         return Circuit::NullGate();
265     }
266 
GetGatesByBcIndex(uint32_t bcIndex)267     const std::vector<GateRef>& GetGatesByBcIndex(uint32_t bcIndex) const
268     {
269         ASSERT(bcIndex < byteCodeToJSGates_.size());
270         return byteCodeToJSGates_[bcIndex];
271     }
272 
GetBcIndexByGate(GateRef gate)273     uint32_t GetBcIndexByGate(GateRef gate) const
274     {
275         return jsGatesToByteCode_.at(gate);
276     }
277 
IsBcIndexByGate(GateRef gate)278     bool IsBcIndexByGate(GateRef gate) const
279     {
280         if (jsGatesToByteCode_.find(gate) == jsGatesToByteCode_.end()) {
281             return false;
282         }
283         return true;
284     }
285 
NeedCheckSafePointAndStackOver()286     bool NeedCheckSafePointAndStackOver() const
287     {
288         return !isInline_ && !method_->IsNoGC();
289     }
290 
UpdateBcIndexGate(GateRef gate,uint32_t bcIndex)291     void UpdateBcIndexGate(GateRef gate, uint32_t bcIndex)
292     {
293         ASSERT(gateAcc_.GetOpCode(gate) == OpCode::JS_BYTECODE);
294         ASSERT(bcIndex < byteCodeToJSGates_.size());
295         byteCodeToJSGates_[bcIndex].emplace_back(gate);
296         jsGatesToByteCode_[gate] = bcIndex;
297     }
298 
GetMethod()299     [[nodiscard]] const MethodLiteral* GetMethod() const
300     {
301         return method_;
302     }
303 
GetJSPandaFile()304     [[nodiscard]] const JSPandaFile *GetJSPandaFile() const
305     {
306         return file_;
307     }
308 
GetMethodName()309     const std::string& GetMethodName() const
310     {
311         return methodName_;
312     }
313 
IsLogEnabled()314     bool IsLogEnabled() const
315     {
316         return enableLog_;
317     }
318 
IsTypeLoweringEnabled()319     bool IsTypeLoweringEnabled() const
320     {
321         return enableTypeLowering_;
322     }
323 
GetAsyncRelatedGates()324     [[nodiscard]] const std::vector<GateRef>& GetAsyncRelatedGates() const
325     {
326         return suspendAndResumeGates_;
327     }
328 
UpdateAsyncRelatedGate(GateRef gate)329     void UpdateAsyncRelatedGate(GateRef gate)
330     {
331         suspendAndResumeGates_.emplace_back(gate);
332     };
333 
HasTypes()334     inline bool HasTypes() const
335     {
336         return hasTypes_;
337     }
338 
339     template <class Callback>
EnumerateBlock(BytecodeRegion & bb,const Callback & cb)340     void EnumerateBlock(BytecodeRegion &bb, const Callback &cb)
341     {
342         auto &iterator = bb.GetBytecodeIterator();
343         iterator.GotoStart();
344         while (!iterator.Done()) {
345             auto &bytecodeInfo = iterator.GetBytecodeInfo();
346             bool ret = cb(bytecodeInfo);
347             if (!ret) {
348                 break;
349             }
350             ++iterator;
351         }
352     }
353 
GetBasicBlockById(size_t id)354     BytecodeRegion &GetBasicBlockById(size_t id)
355     {
356         ASSERT(id < graph_.size());
357         return RegionAt(id);
358     }
359 
AddBasicBlock(BytecodeRegion * region)360     void AddBasicBlock(BytecodeRegion* region)
361     {
362         graph_.emplace_back(region);
363     }
364 
GetBasicBlockCount()365     size_t GetBasicBlockCount() const
366     {
367         return graph_.size();
368     }
369 
GetPcOffset(const uint8_t * pc)370     size_t GetPcOffset(const uint8_t *pc) const
371     {
372         return static_cast<size_t>(pc - method_->GetBytecodeArray());
373     }
374 
GetPcOffset(uint32_t bcIndex)375     size_t GetPcOffset(uint32_t bcIndex) const
376     {
377         const uint8_t* pc = GetPCByIndex(bcIndex);
378         return GetPcOffset(pc);
379     }
380 
GetPcOffsetByGate(GateRef gate)381     size_t GetPcOffsetByGate(GateRef gate) const
382     {
383         return GetPcOffset(jsGatesToByteCode_.at(gate));
384     }
385 
GetElementsKindsForUser(GateRef gate)386     std::vector<ElementsKind> GetElementsKindsForUser(GateRef gate) const
387     {
388         return pgoTypeRecorder_.GetElementsKindsForUser(GetPcOffsetByGate(gate));
389     }
390 
GetElementsKindForCreater(GateRef gate)391     ElementsKind GetElementsKindForCreater(GateRef gate) const
392     {
393         return pgoTypeRecorder_.GetElementsKindForCreater(gateAcc_.TryGetPcOffset(gate));
394     }
395 
GetArrayElementsLength(GateRef gate)396     uint32_t GetArrayElementsLength(GateRef gate) const
397     {
398         return pgoTypeRecorder_.GetElementsLength(gateAcc_.TryGetPcOffset(gate));
399     }
400 
ShouldPGOTypeInfer(GateRef gate)401     bool ShouldPGOTypeInfer(GateRef gate) const
402     {
403         return jsGatesToByteCode_.find(gate) != jsGatesToByteCode_.end();
404     }
405 
GetNumberVRegs()406     size_t GetNumberVRegs() const
407     {
408         return static_cast<size_t>(method_->GetNumberVRegs());
409     }
410 
GetNumberVRegsWithEnv()411     size_t GetNumberVRegsWithEnv() const
412     {
413         return GetNumberVRegs() + 1; // 1: env variable
414     }
415 
GetEnvVregIdx()416     size_t GetEnvVregIdx() const
417     {
418         return GetNumberVRegs();
419     }
420 
GetBytecodes()421     Bytecodes *GetBytecodes() const
422     {
423         return bytecodes_;
424     }
425 
GetLastBcIndex()426     uint32_t GetLastBcIndex() const
427     {
428         return static_cast<uint32_t>(pcOffsets_.size() - 1);
429     }
430 
GetPCByIndex(uint32_t index)431     const uint8_t *GetPCByIndex(uint32_t index) const
432     {
433         ASSERT(index <= GetLastBcIndex());
434         return pcOffsets_[index];
435     }
436 
GetFirstPC()437     const uint8_t *GetFirstPC() const
438     {
439         return GetPCByIndex(0);
440     }
441 
GetLastPC()442     const uint8_t *GetLastPC() const
443     {
444         return GetPCByIndex(GetLastBcIndex());
445     }
446 
FindBcIndexByPc(const uint8_t * pc)447     uint32_t FindBcIndexByPc(const uint8_t *pc) const
448     {
449         auto begin = pcOffsets_.begin();
450         auto end = pcOffsets_.end();
451         auto it = std::lower_bound(begin, end, pc);
452         ASSERT(it != end);
453         ASSERT(*it == pc);
454         return std::distance(begin, it);
455     }
456 
GetBytecodeInfo(uint32_t index)457     const BytecodeInfo &GetBytecodeInfo(uint32_t index) const
458     {
459         return infoData_[index];
460     }
461 
HasTryCatch()462     bool HasTryCatch() const
463     {
464         return hasTryCatch_;
465     }
466 
EnableLoopOptimization()467     bool EnableLoopOptimization() const
468     {
469         return (!HasTryCatch()) && frameStateBuilder_.HasLoop();
470     }
471 
472     void RemoveUnreachableRegion();
473 
GetFrameArgs()474     GateRef GetFrameArgs() const
475     {
476         return argAcc_.GetFrameArgs();
477     }
478 
GetPreFrameState()479     GateRef GetPreFrameState() const
480     {
481         return preFrameState_;
482     }
483 
SetPreFrameState(GateRef gate)484     void SetPreFrameState(GateRef gate)
485     {
486         preFrameState_ = gate;
487     }
488 
GetPreFrameArgs()489     GateRef GetPreFrameArgs() const
490     {
491         return preFrameArgs_;
492     }
493 
SetPreFrameArgs(GateRef gate)494     void SetPreFrameArgs(GateRef gate)
495     {
496         preFrameArgs_ = gate;
497     }
498 
IsEntryBlock(const size_t bbId)499     inline bool IsEntryBlock(const size_t bbId) const
500     {
501         return bbId == 0;
502     }
503 
IsFirstBasicBlock(const size_t bbId)504     inline bool IsFirstBasicBlock(const size_t bbId) const
505     {
506         return bbId == 1;
507     }
508 
GetTSManager()509     TSManager *GetTSManager() const
510     {
511         return tsManager_;
512     }
513 
GetTypeRecorder()514     const TypeRecorder *GetTypeRecorder() const
515     {
516         return &typeRecorder_;
517     }
518 
GetPGOTypeRecorder()519     const PGOTypeRecorder *GetPGOTypeRecorder() const
520     {
521         return &pgoTypeRecorder_;
522     }
523 
GetArgGate(const size_t currentVreg)524     GateRef GetArgGate(const size_t currentVreg) const
525     {
526         return argAcc_.GetArgGate(currentVreg);
527     }
528 
ArgGateNotExisted(const size_t currentVreg)529     GateRef ArgGateNotExisted(const size_t currentVreg)
530     {
531         return argAcc_.ArgGateNotExisted(currentVreg);
532     }
533 
GetLoopHeaderGates()534     ChunkVector<GateRef>& GetLoopHeaderGates()
535     {
536         return loopHeaderGates_;
537     }
538 
NumberOfLiveBlock()539     size_t NumberOfLiveBlock() const
540     {
541         return numOfLiveBB_;
542     }
543 
544 private:
545     void CollectTryCatchBlockInfo(ExceptionInfo &Exception);
546     void BuildCatchBlocks(const ExceptionInfo &Exception);
547     void BuildEntryBlock();
548     void BuildRegions(const ExceptionInfo &Exception);
549     // build circuit
550     void BuildCircuitArgs();
551     std::vector<GateRef> CreateGateInList(const BytecodeInfo &info, const GateMetaData *meta);
552     GateRef NewConst(const BytecodeInfo &info);
553     void NewJSGate(BytecodeRegion &bb);
554     void NewJump(BytecodeRegion &bbd);
555     GateRef NewReturn(BytecodeRegion &bb);
556     void NewByteCode(BytecodeRegion &bb);
557     void MergeThrowGate(BytecodeRegion &bb, uint32_t bcIndex);
558     void MergeExceptionGete(BytecodeRegion &bb, const BytecodeInfo& bytecodeInfo, uint32_t bcIndex);
559     void BuildSubCircuit();
560 
561     void UpdateCFG();
562     void CollectTryPredsInfo();
563     void ClearUnreachableRegion(ChunkVector<BytecodeRegion*>& pendingList);
564     void RemoveUnusedPredsInfo(BytecodeRegion& bb);
565     void BuildCircuit();
566     void PrintGraph();
567     void PrintBBInfo();
568     void PrintGraph(const char* title);
569     void PrintBytecodeInfo(BytecodeRegion& region);
570     void PrintDefsitesInfo(const std::unordered_map<uint16_t, std::set<size_t>> &defsitesInfo);
571     void BuildRegionInfo();
572     void BuildFrameArgs();
RegionAt(size_t i)573     BytecodeRegion &RegionAt(size_t i)
574     {
575         return *graph_[i];
576     }
577 
578     TSManager *tsManager_;
579     Circuit *circuit_;
580     std::vector<std::vector<GateRef>> byteCodeToJSGates_;
581     std::unordered_map<GateRef, size_t> jsGatesToByteCode_;
582     BytecodeGraph graph_;
583     const JSPandaFile *file_ {nullptr};
584     const MethodLiteral *method_ {nullptr};
585     GateAccessor gateAcc_;
586     ArgumentAccessor argAcc_;
587     TypeRecorder typeRecorder_;
588     PGOTypeRecorder pgoTypeRecorder_;
589     bool hasTypes_ {false};
590     bool enableLog_ {false};
591     bool enableTypeLowering_ {false};
592     std::vector<GateRef> suspendAndResumeGates_ {};
593     std::vector<const uint8_t*> pcOffsets_;
594     FrameStateBuilder frameStateBuilder_;
595     std::string methodName_;
596     const CString &recordName_;
597     Bytecodes *bytecodes_;
598     RegionsInfo regionsInfo_{};
599     std::vector<BytecodeInfo> infoData_ {};
600     bool hasTryCatch_ {false};
601     ChunkVector<GateRef> loopHeaderGates_;
602     GateRef preFrameState_ {Circuit::NullGate()};
603     GateRef preFrameArgs_ {Circuit::NullGate()};
604     size_t numOfLiveBB_ {0};
605     bool isInline_ {false};
606 };
607 }  // namespace panda::ecmascript::kungfu
608 #endif  // ECMASCRIPT_CLASS_LINKER_BYTECODE_CIRCUIT_IR_BUILDER_H
609