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