/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bytecode_circuit_builder.h" #include "ecmascript/ts_types/ts_loader.h" namespace panda::ecmascript::kungfu { void BytecodeCircuitBuilder::BytecodeToCircuit() { auto curPc = pcArray_.front(); auto prePc = curPc; std::map byteCodeCurPrePc; std::vector bytecodeBlockInfos; auto startPc = curPc; bytecodeBlockInfos.emplace_back(startPc, SplitKind::START, std::vector(1, startPc)); byteCodeCurPrePc.insert(std::pair(curPc, prePc)); for (size_t i = 1; i < pcArray_.size() - 1; i++) { curPc = pcArray_[i]; byteCodeCurPrePc.insert(std::pair(curPc, prePc)); prePc = curPc; CollectBytecodeBlockInfo(curPc, bytecodeBlockInfos); } // handle empty byteCodeCurPrePc.insert(std::pair(pcArray_[pcArray_.size() - 1], prePc)); // collect try catch block info auto exceptionInfo = CollectTryCatchBlockInfo(byteCodeCurPrePc, bytecodeBlockInfos); // Complete bytecode block Information CompleteBytecodeBlockInfo(byteCodeCurPrePc, bytecodeBlockInfos); // Building the basic block diagram of bytecode BuildBasicBlocks(exceptionInfo, bytecodeBlockInfos, byteCodeCurPrePc); } void BytecodeCircuitBuilder::CollectBytecodeBlockInfo(uint8_t *pc, std::vector &bytecodeBlockInfos) { auto opcode = static_cast(*pc); switch (opcode) { case EcmaOpcode::JMP_IMM8: { int8_t offset = READ_INST_8_0(); std::vector temp; temp.emplace_back(pc + offset); // current basic block end bytecodeBlockInfos.emplace_back(pc, SplitKind::END, temp); bytecodeBlockInfos.emplace_back(pc + BytecodeOffset::TWO, SplitKind::START, std::vector(1, pc + BytecodeOffset::TWO)); // jump basic block start bytecodeBlockInfos.emplace_back(pc + offset, SplitKind::START, std::vector(1, pc + offset)); } break; case EcmaOpcode::JMP_IMM16: { int16_t offset = READ_INST_16_0(); std::vector temp; temp.emplace_back(pc + offset); bytecodeBlockInfos.emplace_back(pc, SplitKind::END, temp); bytecodeBlockInfos.emplace_back(pc + BytecodeOffset::THREE, SplitKind::START, std::vector(1, pc + BytecodeOffset::THREE)); bytecodeBlockInfos.emplace_back(pc + offset, SplitKind::START, std::vector(1, pc + offset)); } break; case EcmaOpcode::JMP_IMM32: { int32_t offset = READ_INST_32_0(); std::vector temp; temp.emplace_back(pc + offset); bytecodeBlockInfos.emplace_back(pc, SplitKind::END, temp); bytecodeBlockInfos.emplace_back(pc + BytecodeOffset::FIVE, SplitKind::START, std::vector(1, pc + BytecodeOffset::FIVE)); bytecodeBlockInfos.emplace_back(pc + offset, SplitKind::START, std::vector(1, pc + offset)); } break; case EcmaOpcode::JEQZ_IMM8: { std::vector temp; temp.emplace_back(pc + BytecodeOffset::TWO); // first successor int8_t offset = READ_INST_8_0(); temp.emplace_back(pc + offset); // second successor // condition branch current basic block end bytecodeBlockInfos.emplace_back(pc, SplitKind::END, temp); // first branch basic block start bytecodeBlockInfos.emplace_back(pc + BytecodeOffset::TWO, SplitKind::START, std::vector(1, pc + BytecodeOffset::TWO)); // second branch basic block start bytecodeBlockInfos.emplace_back(pc + offset, SplitKind::START, std::vector(1, pc + offset)); } break; case EcmaOpcode::JEQZ_IMM16: { std::vector temp; temp.emplace_back(pc + BytecodeOffset::THREE); // first successor int16_t offset = READ_INST_16_0(); temp.emplace_back(pc + offset); // second successor bytecodeBlockInfos.emplace_back(pc, SplitKind::END, temp); // end bytecodeBlockInfos.emplace_back(pc + BytecodeOffset::THREE, SplitKind::START, std::vector(1, pc + BytecodeOffset::THREE)); bytecodeBlockInfos.emplace_back(pc + offset, SplitKind::START, std::vector(1, pc + offset)); } break; case EcmaOpcode::JNEZ_IMM8: { std::vector temp; temp.emplace_back(pc + BytecodeOffset::TWO); // first successor int8_t offset = READ_INST_8_0(); temp.emplace_back(pc + offset); // second successor bytecodeBlockInfos.emplace_back(pc, SplitKind::END, temp); bytecodeBlockInfos.emplace_back(pc + BytecodeOffset::TWO, SplitKind::START, std::vector(1, pc + BytecodeOffset::TWO)); bytecodeBlockInfos.emplace_back(pc + offset, SplitKind::START, std::vector(1, pc + offset)); } break; case EcmaOpcode::JNEZ_IMM16: { std::vector temp; temp.emplace_back(pc + BytecodeOffset::THREE); // first successor int8_t offset = READ_INST_16_0(); temp.emplace_back(pc + offset); // second successor bytecodeBlockInfos.emplace_back(pc, SplitKind::END, temp); bytecodeBlockInfos.emplace_back(pc + BytecodeOffset::THREE, SplitKind::START, std::vector(1, pc + BytecodeOffset::THREE)); bytecodeBlockInfos.emplace_back(pc + offset, SplitKind::START, std::vector(1, pc + offset)); } break; case EcmaOpcode::RETURN_DYN: case EcmaOpcode::RETURNUNDEFINED_PREF: { bytecodeBlockInfos.emplace_back(pc, SplitKind::END, std::vector(1, pc)); break; } case EcmaOpcode::THROWDYN_PREF: case EcmaOpcode::THROWCONSTASSIGNMENT_PREF_V8: case EcmaOpcode::THROWTHROWNOTEXISTS_PREF: case EcmaOpcode::THROWPATTERNNONCOERCIBLE_PREF: case EcmaOpcode::THROWDELETESUPERPROPERTY_PREF: { bytecodeBlockInfos.emplace_back(pc, SplitKind::END, std::vector(1, pc)); } break; default: break; } } std::map, std::vector> BytecodeCircuitBuilder::CollectTryCatchBlockInfo( std::map &byteCodeCurPrePc, std::vector &bytecodeBlockInfos) { // try contains many catch const panda_file::File *file = file_->GetPandaFile(); std::map, std::vector> byteCodeException; panda_file::MethodDataAccessor mda(*file, method_->GetFileId()); panda_file::CodeDataAccessor cda(*file, mda.GetCodeId().value()); cda.EnumerateTryBlocks([this, &byteCodeCurPrePc, &bytecodeBlockInfos, &byteCodeException]( panda_file::CodeDataAccessor::TryBlock &try_block) { auto tryStartOffset = try_block.GetStartPc(); auto tryEndOffset = try_block.GetStartPc() + try_block.GetLength(); auto tryStartPc = const_cast(method_->GetBytecodeArray() + tryStartOffset); auto tryEndPc = const_cast(method_->GetBytecodeArray() + tryEndOffset); byteCodeException[std::make_pair(tryStartPc, tryEndPc)] = {}; uint32_t pcOffset = panda_file::INVALID_OFFSET; try_block.EnumerateCatchBlocks([&](panda_file::CodeDataAccessor::CatchBlock &catch_block) { pcOffset = catch_block.GetHandlerPc(); auto catchBlockPc = const_cast(method_->GetBytecodeArray() + pcOffset); // try block associate catch block byteCodeException[std::make_pair(tryStartPc, tryEndPc)].emplace_back(catchBlockPc); return true; }); // Check whether the previous block of the try block exists. // If yes, add the current block; otherwise, create a new block. bool flag = false; for (size_t i = 0; i < bytecodeBlockInfos.size(); i++) { if (bytecodeBlockInfos[i].splitKind == SplitKind::START) { continue; } if (bytecodeBlockInfos[i].pc == byteCodeCurPrePc[tryStartPc]) { flag = true; break; } } if (!flag) { // pre block bytecodeBlockInfos.emplace_back(byteCodeCurPrePc[tryStartPc], SplitKind::END, std::vector(1, tryStartPc)); } // try block bytecodeBlockInfos.emplace_back(tryStartPc, SplitKind::START, std::vector(1, tryStartPc)); flag = false; for (size_t i = 0; i < bytecodeBlockInfos.size(); i++) { if (bytecodeBlockInfos[i].splitKind == SplitKind::START) { continue; } if (bytecodeBlockInfos[i].pc == byteCodeCurPrePc[tryEndPc]) { auto &succs = bytecodeBlockInfos[i].succs; auto iter = std::find(succs.begin(), succs.end(), bytecodeBlockInfos[i].pc); if (iter == succs.end()) { auto opcode = static_cast(*(bytecodeBlockInfos[i].pc)); switch (opcode) { case EcmaOpcode::JMP_IMM8: case EcmaOpcode::JMP_IMM16: case EcmaOpcode::JMP_IMM32: case EcmaOpcode::JEQZ_IMM8: case EcmaOpcode::JEQZ_IMM16: case EcmaOpcode::JNEZ_IMM8: case EcmaOpcode::JNEZ_IMM16: case EcmaOpcode::RETURN_DYN: case EcmaOpcode::RETURNUNDEFINED_PREF: case EcmaOpcode::THROWDYN_PREF: { break; } default: { succs.emplace_back(tryEndPc); break; } } } flag = true; break; } } if (!flag) { bytecodeBlockInfos.emplace_back(byteCodeCurPrePc[tryEndPc], SplitKind::END, std::vector(1, tryEndPc)); } bytecodeBlockInfos.emplace_back(tryEndPc, SplitKind::START, std::vector(1, tryEndPc)); // next block return true; }); return byteCodeException; } void BytecodeCircuitBuilder::CompleteBytecodeBlockInfo(std::map &byteCodeCurPrePc, std::vector &bytecodeBlockInfos) { std::sort(bytecodeBlockInfos.begin(), bytecodeBlockInfos.end()); #if ECMASCRIPT_ENABLE_TS_AOT_PRINT PrintCollectBlockInfo(bytecodeBlockInfos); #endif // Deduplicate auto deduplicateIndex = std::unique(bytecodeBlockInfos.begin(), bytecodeBlockInfos.end()); bytecodeBlockInfos.erase(deduplicateIndex, bytecodeBlockInfos.end()); // Supplementary block information std::vector endBlockPc; std::vector startBlockPc; for (size_t i = 0; i < bytecodeBlockInfos.size() - 1; i++) { if (bytecodeBlockInfos[i].splitKind == bytecodeBlockInfos[i + 1].splitKind && bytecodeBlockInfos[i].splitKind == SplitKind::START) { auto prePc = byteCodeCurPrePc[bytecodeBlockInfos[i + 1].pc]; endBlockPc.emplace_back(prePc); // Previous instruction of current instruction endBlockPc.emplace_back(bytecodeBlockInfos[i + 1].pc); // current instruction continue; } if (bytecodeBlockInfos[i].splitKind == bytecodeBlockInfos[i + 1].splitKind && bytecodeBlockInfos[i].splitKind == SplitKind::END) { auto tempPc = bytecodeBlockInfos[i].pc; auto findItem = std::find_if(byteCodeCurPrePc.begin(), byteCodeCurPrePc.end(), [tempPc](const std::map::value_type item) { return item.second == tempPc; }); if (findItem != byteCodeCurPrePc.end()) { startBlockPc.emplace_back((*findItem).first); } } } // Supplementary end block info for (auto iter = endBlockPc.begin(); iter != endBlockPc.end(); iter += 2) { // 2: index bytecodeBlockInfos.emplace_back(*iter, SplitKind::END, std::vector(1, *(iter + 1))); } // Supplementary start block info for (auto iter = startBlockPc.begin(); iter != startBlockPc.end(); iter++) { bytecodeBlockInfos.emplace_back(*iter, SplitKind::START, std::vector(1, *iter)); } // Deduplicate successor for (size_t i = 0; i < bytecodeBlockInfos.size(); i++) { if (bytecodeBlockInfos[i].splitKind == SplitKind::END) { std::set tempSet(bytecodeBlockInfos[i].succs.begin(), bytecodeBlockInfos[i].succs.end()); bytecodeBlockInfos[i].succs.assign(tempSet.begin(), tempSet.end()); } } std::sort(bytecodeBlockInfos.begin(), bytecodeBlockInfos.end()); // handling jumps to an empty block auto endPc = bytecodeBlockInfos[bytecodeBlockInfos.size() - 1].pc; auto iter = --byteCodeCurPrePc.end(); if (endPc == iter->first) { bytecodeBlockInfos.emplace_back(endPc, SplitKind::END, std::vector(1, endPc)); } // Deduplicate deduplicateIndex = std::unique(bytecodeBlockInfos.begin(), bytecodeBlockInfos.end()); bytecodeBlockInfos.erase(deduplicateIndex, bytecodeBlockInfos.end()); #if ECMASCRIPT_ENABLE_TS_AOT_PRINT PrintCollectBlockInfo(bytecodeBlockInfos); #endif } void BytecodeCircuitBuilder::BuildBasicBlocks(std::map, std::vector> &exception, std::vector &bytecodeBlockInfo, [[maybe_unused]] std::map &byteCodeCurPrePc) { std::map startPcToBB; // [start, bb] std::map endPcToBB; // [end, bb] BytecodeGraph byteCodeGraph; auto &blocks = byteCodeGraph.graph; byteCodeGraph.method = method_; blocks.resize(bytecodeBlockInfo.size() / 2); // 2 : half size // build basic block int blockId = 0; int index = 0; for (size_t i = 0; i < bytecodeBlockInfo.size() - 1; i += 2) { // 2:index auto startPc = bytecodeBlockInfo[i].pc; auto endPc = bytecodeBlockInfo[i + 1].pc; auto block = &blocks[index++]; block->id = blockId++; block->start = startPc; block->end = endPc; block->preds = {}; block->succs = {}; startPcToBB[startPc] = block; endPcToBB[endPc] = block; } // add block associate for (size_t i = 0; i < bytecodeBlockInfo.size(); i++) { if (bytecodeBlockInfo[i].splitKind == SplitKind::START) { continue; } auto curPc = bytecodeBlockInfo[i].pc; auto &successors = bytecodeBlockInfo[i].succs; for (size_t j = 0; j < successors.size(); j++) { if (successors[j] == curPc) { continue; } auto curBlock = endPcToBB[curPc]; auto succsBlock = startPcToBB[successors[j]]; curBlock->succs.emplace_back(succsBlock); succsBlock->preds.emplace_back(curBlock); } } // try catch block associate for (size_t i = 0; i < blocks.size(); i++) { auto pc = blocks[i].start; auto it = exception.begin(); for (; it != exception.end(); it++) { if (pc < it->first.first || pc >= it->first.second) { // try block interval continue; } auto catchs = exception[it->first]; // catchs start pc for (size_t j = i + 1; j < blocks.size(); j++) { if (std::find(catchs.begin(), catchs.end(), blocks[j].start) != catchs.end()) { blocks[i].catchs.insert(blocks[i].catchs.begin(), &blocks[j]); blocks[i].succs.emplace_back(&blocks[j]); blocks[j].preds.emplace_back(&blocks[i]); } } } } for (size_t i = 0; i < blocks.size(); i++) { bbIdToBasicBlock_[blocks[i].id] = &blocks[i]; } #if ECMASCRIPT_ENABLE_TS_AOT_PRINT PrintGraph(byteCodeGraph.graph); #endif ComputeDominatorTree(byteCodeGraph); } void BytecodeCircuitBuilder::ComputeDominatorTree(BytecodeGraph &byteCodeGraph) { auto &graph = byteCodeGraph.graph; // Construct graph backward order std::map bbIdToDfsTimestamp; // (basicblock id, dfs order) size_t timestamp = 0; std::deque pendingList; std::vector visited(graph.size(), 0); auto basicBlockId = graph[0].id; pendingList.push_back(basicBlockId); while (!pendingList.empty()) { auto &curBlockId = pendingList.back(); pendingList.pop_back(); bbIdToDfsTimestamp[curBlockId] = timestamp++; for (auto &succBlock: graph[curBlockId].succs) { if (visited[succBlock->id] == 0) { visited[succBlock->id] = 1; pendingList.push_back(succBlock->id); } } } RemoveDeadRegions(bbIdToDfsTimestamp, byteCodeGraph); #if ECMASCRIPT_ENABLE_TS_AOT_PRINT // print cfg order for (auto iter : bbIdToDfsTimestamp) { std::cout << "BB_" << iter.first << " depth is : " << iter.second << std::endl; } #endif std::vector immDom(graph.size()); // immediate dominator std::vector> doms(graph.size()); // dominators set doms[0] = {0}; for (size_t i = 1; i < doms.size(); i++) { doms[i].resize(doms.size()); std::iota(doms[i].begin(), doms[i].end(), 0); } bool changed = true; while (changed) { changed = false; for (size_t i = 1; i < doms.size(); i++) { if (graph[i].isDead) { continue; } auto &curDom = doms[i]; size_t curDomSize = curDom.size(); curDom.resize(doms.size()); std::iota(curDom.begin(), curDom.end(), 0); // traverse the predecessor nodes of the current node, Computing Dominators for (auto &preBlock : graph[i].preds) { std::vector tmp(curDom.size()); auto preDom = doms[preBlock->id]; auto it = std::set_intersection(curDom.begin(), curDom.end(), preDom.begin(), preDom.end(), tmp.begin()); tmp.resize(it - tmp.begin()); curDom = tmp; } auto it = std::find(curDom.begin(), curDom.end(), i); if (it == curDom.end()) { curDom.push_back(i); std::sort(curDom.begin(), curDom.end()); } if (doms[i].size() != curDomSize) { changed = true; } } } #if ECMASCRIPT_ENABLE_TS_AOT_PRINT // print dominators set for (size_t i = 0; i < doms.size(); i++) { std::cout << "block " << i << " dominator blocks has: "; for (auto j: doms[i]) { std::cout << j << " , "; } std::cout << std::endl; } #endif // compute immediate dominator immDom[0] = doms[0].front(); for (size_t i = 1; i < doms.size(); i++) { if (graph[i].isDead) { continue; } auto it = std::remove(doms[i].begin(), doms[i].end(), i); doms[i].resize(it - doms[i].begin()); immDom[i] = *std::max_element( doms[i].begin(), doms[i].end(), [graph, bbIdToDfsTimestamp](size_t lhs, size_t rhs) -> bool { return bbIdToDfsTimestamp.at(graph[lhs].id) < bbIdToDfsTimestamp.at(graph[rhs].id); }); } #if ECMASCRIPT_ENABLE_TS_AOT_PRINT // print immediate dominator for (size_t i = 0; i < immDom.size(); i++) { std::cout << i << " immediate dominator: " << immDom[i] << std::endl; } PrintGraph(graph); #endif BuildImmediateDominator(immDom, byteCodeGraph); } void BytecodeCircuitBuilder::BuildImmediateDominator(std::vector &immDom, BytecodeGraph &byteCodeGraph) { auto &graph = byteCodeGraph.graph; graph[0].iDominator = &graph[0]; for (size_t i = 1; i < immDom.size(); i++) { auto dominatedBlock = bbIdToBasicBlock_.at(i); if (dominatedBlock->isDead) { continue; } auto immDomBlock = bbIdToBasicBlock_.at(immDom[i]); dominatedBlock->iDominator = immDomBlock; } #if ECMASCRIPT_ENABLE_TS_AOT_PRINT for (auto block : graph) { if (block.isDead) { continue; } std::cout << "current block " << block.id << " immediate dominator block id: " << block.iDominator->id << std::endl; } #endif for (auto &block : graph) { if (block.isDead) { continue; } if (block.iDominator->id != block.id) { block.iDominator->immDomBlocks.emplace_back(&block); } } #if ECMASCRIPT_ENABLE_TS_AOT_PRINT for (auto &block : graph) { if (block.isDead) { continue; } std::cout << "block " << block.id << " dominate block has: "; for (size_t i = 0; i < block.immDomBlocks.size(); i++) { std::cout << block.immDomBlocks[i]->id << ","; } std::cout << std::endl; } #endif ComputeDomFrontiers(immDom, byteCodeGraph); InsertPhi(byteCodeGraph); UpdateCFG(byteCodeGraph); BuildCircuit(byteCodeGraph); } void BytecodeCircuitBuilder::ComputeDomFrontiers(std::vector &immDom, BytecodeGraph &byteCodeGraph) { auto &graph = byteCodeGraph.graph; std::vector> domFrontiers(immDom.size()); for (auto &bb : graph) { if (bb.isDead) { continue; } if (bb.preds.size() < 2) { // 2: pred num continue; } for (size_t i = 0; i < bb.preds.size(); i++) { auto runner = bb.preds[i]; while (runner->id != immDom[bb.id]) { domFrontiers[runner->id].insert(&bb); runner = bbIdToBasicBlock_.at(immDom[runner->id]); } } } for (size_t i = 0; i < domFrontiers.size(); i++) { for (auto iter = domFrontiers[i].begin(); iter != domFrontiers[i].end(); iter++) { graph[i].domFrontiers.emplace_back(*iter); } } #if ECMASCRIPT_ENABLE_TS_AOT_PRINT for (size_t i = 0; i < domFrontiers.size(); i++) { std::cout << "basic block " << i << " dominate Frontiers is: "; for (auto iter = domFrontiers[i].begin(); iter != domFrontiers[i].end(); iter++) { std::cout << (*iter)->id << " , "; } std::cout << std::endl; } #endif } void BytecodeCircuitBuilder::RemoveDeadRegions(const std::map &bbIdToDfsTimestamp, BytecodeGraph &byteCodeGraph) { auto &graph = byteCodeGraph.graph; for (auto &block: graph) { std::vector newPreds; for (auto &bb : block.preds) { if (bbIdToDfsTimestamp.count(bb->id)) { newPreds.emplace_back(bb); } } block.preds = newPreds; } for (auto &block : graph) { block.isDead = !bbIdToDfsTimestamp.count(block.id); if (block.isDead) { block.succs.clear(); } } } BytecodeInfo BytecodeCircuitBuilder::GetBytecodeInfo(uint8_t *pc) { BytecodeInfo info; auto opcode = static_cast(*pc); info.opcode = opcode; switch (opcode) { case EcmaOpcode::MOV_V4_V4: { uint16_t vdst = READ_INST_4_0(); uint16_t vsrc = READ_INST_4_1(); info.vregOut.emplace_back(vdst); info.offset = BytecodeOffset::TWO; info.inputs.emplace_back(VirtualRegister(vsrc)); break; } case EcmaOpcode::MOV_DYN_V8_V8: { uint16_t vdst = READ_INST_8_0(); uint16_t vsrc = READ_INST_8_1(); info.vregOut.emplace_back(vdst); info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(vsrc)); break; } case EcmaOpcode::MOV_DYN_V16_V16: { uint16_t vdst = READ_INST_16_0(); uint16_t vsrc = READ_INST_16_2(); info.vregOut.emplace_back(vdst); info.offset = BytecodeOffset::FIVE; info.inputs.emplace_back(VirtualRegister(vsrc)); break; } case EcmaOpcode::LDA_STR_ID32: { info.accOut = true; info.offset = BytecodeOffset::FIVE; uint64_t imm = READ_INST_32_0(); info.inputs.emplace_back(StringId(imm)); break; } case EcmaOpcode::JMP_IMM8: { info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::JMP_IMM16: { info.offset = BytecodeOffset::THREE; break; } case EcmaOpcode::JMP_IMM32: { info.offset = BytecodeOffset::FIVE; break; } case EcmaOpcode::JEQZ_IMM8: { info.accIn = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::JEQZ_IMM16: { info.accIn = true; info.offset = BytecodeOffset::THREE; break; } case EcmaOpcode::JNEZ_IMM8: { info.accIn = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::JNEZ_IMM16: { info.accIn = true; info.offset = BytecodeOffset::THREE; break; } case EcmaOpcode::LDA_DYN_V8: { uint16_t vsrc = READ_INST_8_0(); info.accOut = true; info.offset = BytecodeOffset::TWO; info.inputs.emplace_back(VirtualRegister(vsrc)); break; } case EcmaOpcode::STA_DYN_V8: { uint16_t vdst = READ_INST_8_0(); info.vregOut.emplace_back(vdst); info.accIn = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDAI_DYN_IMM32: { info.accOut = true; info.offset = BytecodeOffset::FIVE; info.inputs.emplace_back(Immediate(READ_INST_32_0())); break; } case EcmaOpcode::FLDAI_DYN_IMM64: { info.accOut = true; info.offset = BytecodeOffset::NINE; info.inputs.emplace_back(Immediate(READ_INST_64_0())); break; } case EcmaOpcode::CALLARG0DYN_PREF_V8: { uint32_t funcReg = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(funcReg)); break; } case EcmaOpcode::CALLARG1DYN_PREF_V8_V8: { uint32_t funcReg = READ_INST_8_1(); uint32_t reg = READ_INST_8_2(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(funcReg)); info.inputs.emplace_back(VirtualRegister(reg)); break; } case EcmaOpcode::CALLARGS2DYN_PREF_V8_V8_V8: { uint32_t funcReg = READ_INST_8_1(); uint32_t reg0 = READ_INST_8_2(); uint32_t reg1 = READ_INST_8_3(); info.accOut = true; info.offset = BytecodeOffset::FIVE; info.inputs.emplace_back(VirtualRegister(funcReg)); info.inputs.emplace_back(VirtualRegister(reg0)); info.inputs.emplace_back(VirtualRegister(reg1)); break; } case EcmaOpcode::CALLARGS3DYN_PREF_V8_V8_V8_V8: { uint32_t funcReg = READ_INST_8_1(); uint32_t reg0 = READ_INST_8_2(); uint32_t reg1 = READ_INST_8_3(); uint32_t reg2 = READ_INST_8_4(); info.accOut = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(VirtualRegister(funcReg)); info.inputs.emplace_back(VirtualRegister(reg0)); info.inputs.emplace_back(VirtualRegister(reg1)); info.inputs.emplace_back(VirtualRegister(reg2)); break; } case EcmaOpcode::CALLITHISRANGEDYN_PREF_IMM16_V8: { uint32_t funcReg = READ_INST_8_3(); uint32_t actualNumArgs = READ_INST_16_1(); info.inputs.emplace_back(Immediate(actualNumArgs)); info.inputs.emplace_back(VirtualRegister(funcReg)); for (size_t i = 1; i <= actualNumArgs; i++) { info.inputs.emplace_back(VirtualRegister(funcReg + i)); } info.accOut = true; info.offset = BytecodeOffset::FIVE; break; } case EcmaOpcode::CALLSPREADDYN_PREF_V8_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); uint16_t v2 = READ_INST_8_3(); info.accOut = true; info.offset = BytecodeOffset::FIVE; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); info.inputs.emplace_back(VirtualRegister(v2)); break; } case EcmaOpcode::CALLIRANGEDYN_PREF_IMM16_V8: { uint32_t funcReg = READ_INST_8_3(); uint32_t actualNumArgs = READ_INST_16_1(); info.inputs.emplace_back(Immediate(actualNumArgs)); info.inputs.emplace_back(VirtualRegister(funcReg)); for (size_t i = 1; i <= actualNumArgs; i++) { info.inputs.emplace_back(VirtualRegister(funcReg + i)); } info.accOut = true; info.offset = BytecodeOffset::FIVE; break; } case EcmaOpcode::RETURN_DYN: { info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::ONE; break; } case EcmaOpcode::RETURNUNDEFINED_PREF: { info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDNAN_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDINFINITY_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDGLOBALTHIS_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDUNDEFINED_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDNULL_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDSYMBOL_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDGLOBAL_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDTRUE_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDFALSE_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::LDLEXENVDYN_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::GETUNMAPPEDARGS_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::ASYNCFUNCTIONENTER_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::TONUMBER_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::NEGDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::NOTDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::INCDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::DECDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::THROWDYN_PREF: { info.accIn = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::TYPEOFDYN_PREF: { info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::GETPROPITERATOR_PREF: { info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::RESUMEGENERATOR_PREF_V8: { uint16_t vs = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(vs)); break; } case EcmaOpcode::GETRESUMEMODE_PREF_V8: { uint16_t vs = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(vs)); break; } case EcmaOpcode::GETITERATOR_PREF: { info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::THROWCONSTASSIGNMENT_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::THROWTHROWNOTEXISTS_PREF: { info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::THROWPATTERNNONCOERCIBLE_PREF: { info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::THROWIFNOTOBJECT_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::ITERNEXT_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::CLOSEITERATOR_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::ADD2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::SUB2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::MUL2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::DIV2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::MOD2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::EQDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::NOTEQDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::LESSDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::LESSEQDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::GREATERDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::GREATEREQDYN_PREF_V8: { uint16_t vs = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(vs)); break; } case EcmaOpcode::SHL2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::SHR2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::ASHR2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::AND2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::OR2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::XOR2DYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::DELOBJPROP_PREF_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::DEFINEFUNCDYN_PREF_ID16_IMM16_V8: { uint16_t v0 = READ_INST_8_5(); info.accOut = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(MethodId(READ_INST_16_1())); info.inputs.emplace_back(Immediate(READ_INST_16_3())); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::DEFINENCFUNCDYN_PREF_ID16_IMM16_V8: { uint16_t methodId = READ_INST_16_1(); uint16_t length = READ_INST_16_3(); uint16_t v0 = READ_INST_8_5(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(MethodId(methodId)); info.inputs.emplace_back(Immediate(length)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::DEFINEMETHOD_PREF_ID16_IMM16_V8: { uint16_t methodId = READ_INST_16_1(); uint16_t length = READ_INST_16_3(); uint16_t v0 = READ_INST_8_5(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(MethodId(methodId)); info.inputs.emplace_back(Immediate(length)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::NEWOBJDYNRANGE_PREF_IMM16_V8: { uint16_t firstArgRegIdx = READ_INST_8_3(); info.accOut = true; info.offset = BytecodeOffset::FIVE; info.inputs.emplace_back(Immediate(READ_INST_16_1())); info.inputs.emplace_back(VirtualRegister(firstArgRegIdx)); info.inputs.emplace_back(VirtualRegister(firstArgRegIdx + 1)); break; } case EcmaOpcode::EXPDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::ISINDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::INSTANCEOFDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::STRICTNOTEQDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::STRICTEQDYN_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::LDLEXVARDYN_PREF_IMM16_IMM16: { uint16_t level = READ_INST_16_1(); uint16_t slot = READ_INST_16_3(); info.accOut = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(Immediate(level)); info.inputs.emplace_back(Immediate(slot)); break; } case EcmaOpcode::LDLEXVARDYN_PREF_IMM8_IMM8: { uint16_t level = READ_INST_8_1(); uint16_t slot = READ_INST_8_2(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(Immediate(level)); info.inputs.emplace_back(Immediate(slot)); break; } case EcmaOpcode::LDLEXVARDYN_PREF_IMM4_IMM4: { uint16_t level = READ_INST_4_2(); uint16_t slot = READ_INST_4_3(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(Immediate(level)); info.inputs.emplace_back(Immediate(slot)); break; } case EcmaOpcode::STLEXVARDYN_PREF_IMM16_IMM16_V8: { uint16_t level = READ_INST_16_1(); uint16_t slot = READ_INST_16_3(); uint16_t v0 = READ_INST_8_5(); info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(Immediate(level)); info.inputs.emplace_back(Immediate(slot)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::STLEXVARDYN_PREF_IMM8_IMM8_V8: { uint16_t level = READ_INST_8_1(); uint16_t slot = READ_INST_8_2(); uint16_t v0 = READ_INST_8_3(); info.offset = BytecodeOffset::FIVE; info.inputs.emplace_back(Immediate(level)); info.inputs.emplace_back(Immediate(slot)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::STLEXVARDYN_PREF_IMM4_IMM4_V8: { uint16_t level = READ_INST_4_2(); uint16_t slot = READ_INST_4_3(); uint16_t v0 = READ_INST_8_2(); info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(Immediate(level)); info.inputs.emplace_back(Immediate(slot)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::NEWLEXENVDYN_PREF_IMM16: { uint16_t numVars = READ_INST_16_1(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(Immediate(numVars)); break; } case EcmaOpcode::POPLEXENVDYN_PREF: { info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::CREATEITERRESULTOBJ_PREF_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::SUSPENDGENERATOR_PREF_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::ASYNCFUNCTIONAWAITUNCAUGHT_PREF_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::ASYNCFUNCTIONRESOLVE_PREF_V8_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v2 = READ_INST_8_3(); info.accOut = true; info.offset = BytecodeOffset::FIVE; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v2)); break; } case EcmaOpcode::ASYNCFUNCTIONREJECT_PREF_V8_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v2 = READ_INST_8_3(); info.offset = BytecodeOffset::FIVE; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v2)); break; } case EcmaOpcode::NEWOBJSPREADDYN_PREF_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::THROWUNDEFINEDIFHOLE_PREF_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::STOWNBYNAME_PREF_ID32_V8: { uint32_t stringId = READ_INST_32_1(); uint32_t v0 = READ_INST_8_5(); info.accIn = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(StringId(stringId)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::CREATEEMPTYARRAY_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::CREATEEMPTYOBJECT_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::CREATEOBJECTWITHBUFFER_PREF_IMM16: { uint16_t imm = READ_INST_16_1(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(Immediate(imm)); break; } case EcmaOpcode::SETOBJECTWITHPROTO_PREF_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::CREATEARRAYWITHBUFFER_PREF_IMM16: { uint16_t imm = READ_INST_16_1(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(Immediate(imm)); break; } case EcmaOpcode::GETMODULENAMESPACE_PREF_ID32: { uint32_t stringId = READ_INST_32_1(); info.accOut = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(StringId(stringId)); break; } case EcmaOpcode::STMODULEVAR_PREF_ID32: { uint32_t stringId = READ_INST_32_1(); info.accIn = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(StringId(stringId)); break; } case EcmaOpcode::COPYMODULE_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::LDMODULEVAR_PREF_ID32_IMM8: { uint32_t stringId = READ_INST_32_1(); uint8_t innerFlag = READ_INST_8_5(); info.accOut = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(StringId(stringId)); info.inputs.emplace_back(Immediate(innerFlag)); break; } case EcmaOpcode::CREATEREGEXPWITHLITERAL_PREF_ID32_IMM8: { uint32_t stringId = READ_INST_32_1(); uint8_t flags = READ_INST_8_5(); info.accOut = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(StringId(stringId)); info.inputs.emplace_back(Immediate(flags)); break; } case EcmaOpcode::GETTEMPLATEOBJECT_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::GETNEXTPROPNAME_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::COPYDATAPROPERTIES_PREF_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::STOWNBYINDEX_PREF_V8_IMM32: { uint32_t v0 = READ_INST_8_1(); uint32_t index = READ_INST_32_2(); info.accIn = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(Immediate(index)); break; } case EcmaOpcode::STOWNBYVALUE_PREF_V8_V8: { uint32_t v0 = READ_INST_8_1(); uint32_t v1 = READ_INST_8_2(); info.accIn = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::CREATEOBJECTWITHEXCLUDEDKEYS_PREF_IMM16_V8_V8: { uint16_t numKeys = READ_INST_16_1(); uint16_t v0 = READ_INST_8_3(); info.accOut = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(Immediate(numKeys)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::DEFINEGENERATORFUNC_PREF_ID16_IMM16_V8: { uint16_t methodId = READ_INST_16_1(); uint16_t length = READ_INST_16_3(); uint16_t v0 = READ_INST_8_5(); info.accOut = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(MethodId(methodId)); info.inputs.emplace_back(Immediate(length)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::DEFINEASYNCFUNC_PREF_ID16_IMM16_V8: { uint16_t methodId = READ_INST_16_1(); uint16_t length = READ_INST_16_3(); uint16_t v0 = READ_INST_8_5(); info.accOut = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(MethodId(methodId)); info.inputs.emplace_back(Immediate(length)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::LDHOLE_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::COPYRESTARGS_PREF_IMM16: { uint16_t restIdx = READ_INST_16_1(); info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(Immediate(restIdx)); break; } case EcmaOpcode::DEFINEGETTERSETTERBYVALUE_PREF_V8_V8_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); uint16_t v2 = READ_INST_8_3(); uint16_t v3 = READ_INST_8_4(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); info.inputs.emplace_back(VirtualRegister(v2)); info.inputs.emplace_back(VirtualRegister(v3)); break; } case EcmaOpcode::LDOBJBYINDEX_PREF_V8_IMM32: { uint16_t v0 = READ_INST_8_1(); uint32_t idx = READ_INST_32_2(); info.accOut = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(Immediate(idx)); break; } case EcmaOpcode::STOBJBYINDEX_PREF_V8_IMM32: { uint16_t v0 = READ_INST_8_1(); uint32_t index = READ_INST_32_2(); info.accIn = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(Immediate(index)); break; } case EcmaOpcode::LDOBJBYVALUE_PREF_V8_V8: { uint32_t v0 = READ_INST_8_1(); uint32_t v1 = READ_INST_8_2(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::STOBJBYVALUE_PREF_V8_V8: { uint32_t v0 = READ_INST_8_1(); uint32_t v1 = READ_INST_8_2(); info.accIn = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::LDSUPERBYVALUE_PREF_V8_V8: { uint32_t v0 = READ_INST_8_1(); uint32_t v1 = READ_INST_8_2(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::STSUPERBYVALUE_PREF_V8_V8: { uint32_t v0 = READ_INST_8_1(); uint32_t v1 = READ_INST_8_2(); info.accIn = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::TRYLDGLOBALBYNAME_PREF_ID32: { info.accOut = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(StringId(READ_INST_32_1())); break; } case EcmaOpcode::TRYSTGLOBALBYNAME_PREF_ID32: { info.accIn = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(StringId(READ_INST_32_1())); break; } case EcmaOpcode::STCONSTTOGLOBALRECORD_PREF_ID32: { info.accIn = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(StringId(READ_INST_32_1())); break; } case EcmaOpcode::STLETTOGLOBALRECORD_PREF_ID32: { info.accIn = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(StringId(READ_INST_32_1())); break; } case EcmaOpcode::STCLASSTOGLOBALRECORD_PREF_ID32: { info.accIn = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(StringId(READ_INST_32_1())); break; } case EcmaOpcode::STOWNBYVALUEWITHNAMESET_PREF_V8_V8: { uint32_t v0 = READ_INST_8_1(); uint32_t v1 = READ_INST_8_2(); info.accIn = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::STOWNBYNAMEWITHNAMESET_PREF_ID32_V8: { uint32_t stringId = READ_INST_32_1(); uint32_t v0 = READ_INST_8_5(); info.accIn = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(StringId(stringId)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::LDGLOBALVAR_PREF_ID32: { uint32_t stringId = READ_INST_32_1(); info.accOut = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(StringId(stringId)); break; } case EcmaOpcode::LDOBJBYNAME_PREF_ID32_V8: { uint32_t stringId = READ_INST_32_1(); uint32_t v0 = READ_INST_8_5(); info.accOut = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(StringId(stringId)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::STOBJBYNAME_PREF_ID32_V8: { uint32_t stringId = READ_INST_32_1(); uint32_t v0 = READ_INST_8_5(); info.accIn = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(StringId(stringId)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::LDSUPERBYNAME_PREF_ID32_V8: { uint32_t stringId = READ_INST_32_1(); uint32_t v0 = READ_INST_8_5(); info.accOut = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(StringId(stringId)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::STSUPERBYNAME_PREF_ID32_V8: { uint32_t stringId = READ_INST_32_1(); uint32_t v0 = READ_INST_8_5(); info.accIn = true; info.offset = BytecodeOffset::SEVEN; info.inputs.emplace_back(StringId(stringId)); info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::STGLOBALVAR_PREF_ID32: { uint32_t stringId = READ_INST_32_1(); info.accIn = true; info.offset = BytecodeOffset::SIX; info.inputs.emplace_back(StringId(stringId)); break; } case EcmaOpcode::CREATEGENERATOROBJ_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::STARRAYSPREAD_PREF_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::GETITERATORNEXT_PREF_V8_V8: { uint16_t v0 = READ_INST_8_1(); uint16_t v1 = READ_INST_8_2(); info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::DEFINECLASSWITHBUFFER_PREF_ID16_IMM16_IMM16_V8_V8: { uint16_t methodId = READ_INST_16_1(); uint16_t imm = READ_INST_16_3(); uint16_t length = READ_INST_16_5(); uint16_t v0 = READ_INST_8_7(); uint16_t v1 = READ_INST_8_8(); info.accOut = true; info.offset = BytecodeOffset::TEN; info.inputs.emplace_back(MethodId(methodId)); info.inputs.emplace_back(Immediate(imm)); info.inputs.emplace_back(Immediate(length)); info.inputs.emplace_back(VirtualRegister(v0)); info.inputs.emplace_back(VirtualRegister(v1)); break; } case EcmaOpcode::LDFUNCTION_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::SUPERCALL_PREF_IMM16_V8: { uint16_t range = READ_INST_16_1(); uint16_t v0 = READ_INST_8_3(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::FIVE; info.inputs.emplace_back(Immediate(range)); info.inputs.emplace_back(Immediate(v0)); break; } case EcmaOpcode::SUPERCALLSPREAD_PREF_V8: { uint16_t v0 = READ_INST_8_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::THREE; info.inputs.emplace_back(VirtualRegister(v0)); break; } case EcmaOpcode::CREATEOBJECTHAVINGMETHOD_PREF_IMM16: { uint16_t imm = READ_INST_16_1(); info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(Immediate(imm)); break; } case EcmaOpcode::THROWIFSUPERNOTCORRECTCALL_PREF_IMM16: { uint16_t imm = READ_INST_16_1(); info.accIn = true; info.offset = BytecodeOffset::FOUR; info.inputs.emplace_back(Immediate(imm)); break; } case EcmaOpcode::LDHOMEOBJECT_PREF: { info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::THROWDELETESUPERPROPERTY_PREF: { info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::DEBUGGER_PREF: { info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::ISTRUE_PREF: { info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::TWO; break; } case EcmaOpcode::ISFALSE_PREF: { info.accIn = true; info.accOut = true; info.offset = BytecodeOffset::TWO; break; } default: { std::cout << opcode << std::endl; abort(); break; } } return info; } void BytecodeCircuitBuilder::InsertPhi(BytecodeGraph &byteCodeGraph) { auto &graph = byteCodeGraph.graph; std::map> defsitesInfo; // for (auto &bb : graph) { if (bb.isDead) { continue; } auto pc = bb.start; while (pc <= bb.end) { auto bytecodeInfo = GetBytecodeInfo(pc); pc = pc + bytecodeInfo.offset; // next inst start pc for (const auto &vreg: bytecodeInfo.vregOut) { defsitesInfo[vreg].insert(bb.id); } } } #if ECMASCRIPT_ENABLE_TS_AOT_PRINT for (const auto&[variable, defsites] : defsitesInfo) { std::cout << "variable: " << variable << " locate block have: "; for (auto id : defsites) { std::cout << id << " , "; } std::cout << std::endl; } #endif for (const auto&[variable, defsites] : defsitesInfo) { std::queue workList; for (auto blockId: defsites) { workList.push(blockId); } while (!workList.empty()) { auto currentId = workList.front(); workList.pop(); for (auto &block : graph[currentId].domFrontiers) { if (!block->phi.count(variable)) { block->phi.insert(variable); if (!defsitesInfo[variable].count(block->id)) { workList.push(block->id); } } } } } #if ECMASCRIPT_ENABLE_TS_AOT_PRINT PrintGraph(graph); #endif } // Update CFG's predecessor, successor and try catch associations void BytecodeCircuitBuilder::UpdateCFG(BytecodeGraph &byteCodeGraph) { auto &graph = byteCodeGraph.graph; for (auto &bb: graph) { if (bb.isDead) { continue; } bb.preds.clear(); bb.trys.clear(); std::vector newSuccs; for (const auto &succ: bb.succs) { if (std::count(bb.catchs.begin(), bb.catchs.end(), succ)) { continue; } newSuccs.push_back(succ); } bb.succs = newSuccs; } for (auto &bb: graph) { if (bb.isDead) { continue; } for (auto &succ: bb.succs) { succ->preds.push_back(&bb); } for (auto &catchBlock: bb.catchs) { catchBlock->trys.push_back(&bb); } } } bool BytecodeCircuitBuilder::IsJump(EcmaOpcode opcode) { switch (opcode) { case EcmaOpcode::JMP_IMM8: case EcmaOpcode::JMP_IMM16: case EcmaOpcode::JMP_IMM32: case EcmaOpcode::JEQZ_IMM8: case EcmaOpcode::JEQZ_IMM16: case EcmaOpcode::JNEZ_IMM8: case EcmaOpcode::JNEZ_IMM16: return true; default: return false; } } bool BytecodeCircuitBuilder::IsCondJump(EcmaOpcode opcode) { switch (opcode) { case EcmaOpcode::JEQZ_IMM8: case EcmaOpcode::JEQZ_IMM16: case EcmaOpcode::JNEZ_IMM8: case EcmaOpcode::JNEZ_IMM16: return true; default: return false; } } bool BytecodeCircuitBuilder::IsMov(EcmaOpcode opcode) { switch (opcode) { case EcmaOpcode::MOV_V4_V4: case EcmaOpcode::MOV_DYN_V8_V8: case EcmaOpcode::MOV_DYN_V16_V16: case EcmaOpcode::LDA_DYN_V8: case EcmaOpcode::STA_DYN_V8: return true; default: return false; } } bool BytecodeCircuitBuilder::IsReturn(EcmaOpcode opcode) { switch (opcode) { case EcmaOpcode::RETURN_DYN: case EcmaOpcode::RETURNUNDEFINED_PREF: return true; default: return false; } } bool BytecodeCircuitBuilder::IsThrow(EcmaOpcode opcode) { switch (opcode) { case EcmaOpcode::THROWDYN_PREF: case EcmaOpcode::THROWCONSTASSIGNMENT_PREF_V8: case EcmaOpcode::THROWTHROWNOTEXISTS_PREF: case EcmaOpcode::THROWPATTERNNONCOERCIBLE_PREF: case EcmaOpcode::THROWDELETESUPERPROPERTY_PREF: return true; default: return false; } } bool BytecodeCircuitBuilder::IsGeneral(EcmaOpcode opcode) { return !IsMov(opcode) && !IsJump(opcode) && !IsReturn(opcode) && !IsSetConstant(opcode); } bool BytecodeCircuitBuilder::IsSetConstant(EcmaOpcode opcode) { switch (opcode) { case EcmaOpcode::LDNAN_PREF: case EcmaOpcode::LDINFINITY_PREF: case EcmaOpcode::LDUNDEFINED_PREF: case EcmaOpcode::LDNULL_PREF: case EcmaOpcode::LDTRUE_PREF: case EcmaOpcode::LDFALSE_PREF: case EcmaOpcode::LDHOLE_PREF: case EcmaOpcode::LDAI_DYN_IMM32: case EcmaOpcode::FLDAI_DYN_IMM64: return true; default: return false; } } GateRef BytecodeCircuitBuilder::SetGateConstant(const BytecodeInfo &info) { auto opcode = static_cast(info.opcode); GateRef gate = 0; // ts loader panda::ecmascript::TSLoader* tsLoader = vm_->GetTSLoader(); auto tsType = 0; switch (opcode) { case EcmaOpcode::LDNAN_PREF: tsType = tsLoader->GetPrimitiveGT(TSTypeKind::TS_NUMBER).GetGlobalTSTypeRef(); gate = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::F64, bit_cast(panda::ecmascript::base::NAN_VALUE), {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, static_cast(tsType)); break; case EcmaOpcode::LDINFINITY_PREF: tsType = tsLoader->GetPrimitiveGT(TSTypeKind::TS_NUMBER).GetGlobalTSTypeRef(); gate = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::F64, bit_cast(panda::ecmascript::base::POSITIVE_INFINITY), {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, static_cast(tsType)); break; case EcmaOpcode::LDUNDEFINED_PREF: tsType = tsLoader->GetPrimitiveGT(TSTypeKind::TS_UNDEFINED).GetGlobalTSTypeRef(); gate = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I64, JSTaggedValue::VALUE_UNDEFINED, {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, static_cast(tsType)); break; case EcmaOpcode::LDNULL_PREF: tsType = tsLoader->GetPrimitiveGT(TSTypeKind::TS_NULL).GetGlobalTSTypeRef(); gate = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I64, JSTaggedValue::VALUE_NULL, {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, static_cast(tsType)); break; case EcmaOpcode::LDTRUE_PREF: tsType = tsLoader->GetPrimitiveGT(TSTypeKind::TS_BOOLEAN).GetGlobalTSTypeRef(); gate = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I64, JSTaggedValue::VALUE_TRUE, {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, static_cast(tsType)); break; case EcmaOpcode::LDFALSE_PREF: tsType = tsLoader->GetPrimitiveGT(TSTypeKind::TS_BOOLEAN).GetGlobalTSTypeRef(); gate = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I64, JSTaggedValue::VALUE_FALSE, {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, static_cast(tsType)); break; case EcmaOpcode::LDHOLE_PREF: gate = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I64, JSTaggedValue::VALUE_HOLE, {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, GateType::TAGGED_NO_POINTER); break; case EcmaOpcode::LDAI_DYN_IMM32: tsType = tsLoader->GetPrimitiveGT(TSTypeKind::TS_NUMBER).GetGlobalTSTypeRef(); gate = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I64, std::get(info.inputs[0]).GetValue(), {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, static_cast(tsType)); break; case EcmaOpcode::FLDAI_DYN_IMM64: tsType = tsLoader->GetPrimitiveGT(TSTypeKind::TS_NUMBER).GetGlobalTSTypeRef(); gate = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::F64, std::get(info.inputs.at(0)).GetValue(), {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, static_cast(tsType)); break; default: abort(); } return gate; } void BytecodeCircuitBuilder::BuildCircuit(BytecodeGraph &byteCodeGraph) { auto &graph = byteCodeGraph.graph; #if ECMASCRIPT_ENABLE_TS_AOT_PRINT PrintBBInfo(graph); #endif // create arg gates array const size_t numArgs = byteCodeGraph.method->GetNumArgs(); const size_t offsetArgs = byteCodeGraph.method->GetNumVregs(); const size_t actualNumArgs = GetActualNumArgs(numArgs); std::vector argGates(actualNumArgs); for (size_t argIdx = 0; argIdx < CommonArgIdx::NUM_OF_ARGS; argIdx++) { auto argGate = circuit_.NewGate(OpCode(OpCode::ARG), MachineType::I64, argIdx, {Circuit::GetCircuitRoot(OpCode(OpCode::ARG_LIST))}, GateType::TAGGED_VALUE); argGates.at(argIdx) = argGate; commonArgs_.at(argIdx) = argGate; } for (size_t argIdx = CommonArgIdx::NUM_OF_ARGS; argIdx < actualNumArgs; argIdx++) { argGates.at(argIdx) = circuit_.NewGate(OpCode(OpCode::ARG), MachineType::I64, argIdx, {Circuit::GetCircuitRoot(OpCode(OpCode::ARG_LIST))}, GateType::JS_ANY); } // get number of expanded state predicates of each block // one block-level try catch edge may correspond to multiple bytecode-level edges for (auto &bb: graph) { if (bb.isDead) { continue; } bb.numOfStatePreds = 0; } for (auto &bb: graph) { if (bb.isDead) { continue; } auto pc = bb.start; while (pc <= bb.end) { auto bytecodeInfo = GetBytecodeInfo(pc); pc = pc + bytecodeInfo.offset; // next inst start pc if (IsGeneral(static_cast(bytecodeInfo.opcode))) { if (!bb.catchs.empty()) { bb.catchs.at(0)->numOfStatePreds++; } } } for (auto &succ: bb.succs) { succ->numOfStatePreds++; } } // build head of each block for (auto &bb: graph) { if (bb.isDead) { continue; } if (bb.numOfStatePreds == 0) { bb.stateStart = Circuit::GetCircuitRoot(OpCode(OpCode::STATE_ENTRY)); bb.dependStart = Circuit::GetCircuitRoot(OpCode(OpCode::DEPEND_ENTRY)); } else if (bb.numOfStatePreds == 1) { bb.stateStart = circuit_.NewGate(OpCode(OpCode::ORDINARY_BLOCK), 0, {Circuit::NullGate()}, GateType::EMPTY); bb.dependStart = circuit_.NewGate(OpCode(OpCode::DEPEND_RELAY), 0, {bb.stateStart, Circuit::NullGate()}, GateType::EMPTY); } else { bb.stateStart = circuit_.NewGate(OpCode(OpCode::MERGE), bb.numOfStatePreds, std::vector(bb.numOfStatePreds, Circuit::NullGate()), GateType::EMPTY); bb.dependStart = circuit_.NewGate(OpCode(OpCode::DEPEND_SELECTOR), bb.numOfStatePreds, std::vector(bb.numOfStatePreds + 1, Circuit::NullGate()), GateType::EMPTY); circuit_.NewIn(bb.dependStart, 0, bb.stateStart); } } // build states sub-circuit of each block for (auto &bb: graph) { if (bb.isDead) { continue; } auto stateCur = bb.stateStart; auto dependCur = bb.dependStart; ASSERT(stateCur != Circuit::NullGate()); ASSERT(dependCur != Circuit::NullGate()); if (!bb.trys.empty()) { dependCur = circuit_.NewGate(OpCode(OpCode::GET_EXCEPTION), 0, {dependCur}, GateType::EMPTY); } auto pc = bb.start; while (pc <= bb.end) { auto pcPrev = pc; auto bytecodeInfo = GetBytecodeInfo(pc); pc = pc + bytecodeInfo.offset; // next inst start pc size_t numValueInputs = (bytecodeInfo.accIn ? 1 : 0) + bytecodeInfo.inputs.size(); if (IsSetConstant(static_cast(bytecodeInfo.opcode))) { // handle bytecode command to get constants GateRef gate = SetGateConstant(bytecodeInfo); jsgateToBytecode_[gate] = {bb.id, pcPrev}; } else if (IsGeneral(static_cast(bytecodeInfo.opcode))) { // handle general ecma.* bytecodes GateRef gate = 0; const size_t length = 2; // 2: state and depend on input std::vector inList(length + numValueInputs, Circuit::NullGate()); for (size_t i = 0; i < bytecodeInfo.inputs.size(); i++) { const auto &input = bytecodeInfo.inputs[i]; if (std::holds_alternative(input)) { inList[i + length] = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I16, std::get(input).GetId(), {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, GateType::JS_ANY); } else if (std::holds_alternative(input)) { auto tsLoader = vm_->GetTSLoader(); JSHandle newConstPool(vm_->GetJSThread(), constantPool_.GetTaggedValue()); auto string = newConstPool->GetObjectFromCache(std::get(input).GetId()); uint64_t index = tsLoader->AddConstString(string); inList[i + length] = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I32, index, {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, GateType::JS_ANY); } else if (std::holds_alternative(input)) { inList[i + length] = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I64, std::get(input).GetValue(), {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, GateType::JS_ANY); } else { ASSERT(std::holds_alternative(input)); continue; } } if (!bytecodeInfo.vregOut.empty() || bytecodeInfo.accOut) { gate = circuit_.NewGate(OpCode(OpCode::JS_BYTECODE), MachineType::I64, numValueInputs, inList, GateType::JS_ANY); } else { gate = circuit_.NewGate(OpCode(OpCode::JS_BYTECODE), MachineType::NOVALUE, numValueInputs, inList, GateType::EMPTY); } circuit_.NewIn(gate, 0, stateCur); circuit_.NewIn(gate, 1, dependCur); auto ifSuccess = circuit_.NewGate(OpCode(OpCode::IF_SUCCESS), 0, {gate}, GateType::EMPTY); auto ifException = circuit_.NewGate(OpCode(OpCode::IF_EXCEPTION), 0, {gate}, GateType::EMPTY); if (!bb.catchs.empty()) { auto bbNext = bb.catchs.at(0); circuit_.NewIn(bbNext->stateStart, bbNext->statePredIndex, ifException); circuit_.NewIn(bbNext->dependStart, bbNext->statePredIndex + 1, gate); bbNext->statePredIndex++; bbNext->expandedPreds.push_back( {bb.id, pcPrev, true} ); ASSERT(bbNext->statePredIndex <= bbNext->numOfStatePreds); } else { auto constant = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I64, JSTaggedValue::VALUE_EXCEPTION, {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, GateType::JS_ANY); circuit_.NewGate(OpCode(OpCode::RETURN), 0, {ifException, gate, constant, Circuit::GetCircuitRoot(OpCode(OpCode::RETURN_LIST))}, GateType::JS_ANY); } jsgateToBytecode_[gate] = {bb.id, pcPrev}; if (IsThrow(static_cast(bytecodeInfo.opcode))) { circuit_.NewGate(OpCode(OpCode::RETURN), 0, {ifSuccess, gate, TaggedValue::VALUE_HOLE, Circuit::GetCircuitRoot(OpCode(OpCode::RETURN_LIST))}, GateType::JS_ANY); break; } stateCur = ifSuccess; dependCur = gate; if (pcPrev == bb.end) { auto bbNext = &graph.at(bb.id + 1); circuit_.NewIn(bbNext->stateStart, bbNext->statePredIndex, stateCur); circuit_.NewIn(bbNext->dependStart, bbNext->statePredIndex + 1, dependCur); bbNext->statePredIndex++; bbNext->expandedPreds.push_back( {bb.id, pcPrev, false} ); ASSERT(bbNext->statePredIndex <= bbNext->numOfStatePreds); } } else if (IsJump(static_cast(bytecodeInfo.opcode))) { // handle conditional jump and unconditional jump bytecodes if (IsCondJump(static_cast(bytecodeInfo.opcode))) { GateRef gate = 0; gate = circuit_.NewGate(OpCode(OpCode::JS_BYTECODE), MachineType::NOVALUE, numValueInputs, std::vector(2 + numValueInputs, // 2: state and depend input Circuit::NullGate()), GateType::EMPTY); circuit_.NewIn(gate, 0, stateCur); circuit_.NewIn(gate, 1, dependCur); auto ifTrue = circuit_.NewGate(OpCode(OpCode::IF_TRUE), 0, {gate}, GateType::EMPTY); auto ifFalse = circuit_.NewGate(OpCode(OpCode::IF_FALSE), 0, {gate}, GateType::EMPTY); ASSERT(bb.succs.size() == 2); // 2 : 2 num of successors int bitSet = 0; for (auto &bbNext: bb.succs) { if (bbNext->id == bb.id + 1) { circuit_.NewIn(bbNext->stateStart, bbNext->statePredIndex, ifFalse); circuit_.NewIn(bbNext->dependStart, bbNext->statePredIndex + 1, gate); bbNext->statePredIndex++; bbNext->expandedPreds.push_back( {bb.id, pcPrev, false} ); ASSERT(bbNext->statePredIndex <= bbNext->numOfStatePreds); bitSet |= 1; } else { circuit_.NewIn(bbNext->stateStart, bbNext->statePredIndex, ifTrue); circuit_.NewIn(bbNext->dependStart, bbNext->statePredIndex + 1, gate); bbNext->statePredIndex++; bbNext->expandedPreds.push_back( {bb.id, pcPrev, false} ); ASSERT(bbNext->statePredIndex <= bbNext->numOfStatePreds); bitSet |= 2; // 2:verify } } ASSERT(bitSet == 3); // 3:Verify the number of successor blocks jsgateToBytecode_[gate] = {bb.id, pcPrev}; break; } else { ASSERT(bb.succs.size() == 1); auto bbNext = bb.succs.at(0); circuit_.NewIn(bbNext->stateStart, bbNext->statePredIndex, stateCur); circuit_.NewIn(bbNext->dependStart, bbNext->statePredIndex + 1, dependCur); bbNext->statePredIndex++; bbNext->expandedPreds.push_back( {bb.id, pcPrev, false} ); ASSERT(bbNext->statePredIndex <= bbNext->numOfStatePreds); break; } } else if (static_cast(bytecodeInfo.opcode) == EcmaOpcode::RETURN_DYN) { // handle return.dyn bytecode ASSERT(bb.succs.empty()); auto gate = circuit_.NewGate(OpCode(OpCode::RETURN), 0, {stateCur, dependCur, Circuit::NullGate(), Circuit::GetCircuitRoot(OpCode(OpCode::RETURN_LIST))}, GateType::EMPTY); jsgateToBytecode_[gate] = {bb.id, pcPrev}; break; } else if (static_cast(bytecodeInfo.opcode) == EcmaOpcode::RETURNUNDEFINED_PREF) { // handle returnundefined bytecode ASSERT(bb.succs.empty()); auto constant = circuit_.NewGate(OpCode(OpCode::CONSTANT), MachineType::I64, TaggedValue::VALUE_UNDEFINED, {Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST))}, GateType::JS_ANY); auto gate = circuit_.NewGate(OpCode(OpCode::RETURN), 0, {stateCur, dependCur, constant, Circuit::GetCircuitRoot(OpCode(OpCode::RETURN_LIST))}, GateType::EMPTY); jsgateToBytecode_[gate] = {bb.id, pcPrev}; break; } else if (IsMov(static_cast(bytecodeInfo.opcode))) { // handle mov.dyn lda.dyn sta.dyn bytecodes if (pcPrev == bb.end) { auto bbNext = &graph.at(bb.id + 1); circuit_.NewIn(bbNext->stateStart, bbNext->statePredIndex, stateCur); circuit_.NewIn(bbNext->dependStart, bbNext->statePredIndex + 1, dependCur); bbNext->statePredIndex++; bbNext->expandedPreds.push_back( {bb.id, pcPrev, false} ); ASSERT(bbNext->statePredIndex <= bbNext->numOfStatePreds); } } else { abort(); } } } // verification of soundness of CFG for (auto &bb: graph) { if (bb.isDead) { continue; } ASSERT(bb.statePredIndex == bb.numOfStatePreds); } for (auto &bb: graph) { if (bb.isDead) { continue; } bb.phiAcc = (bb.numOfStatePreds > 1) || (!bb.trys.empty()); } #if ECMASCRIPT_ENABLE_TS_AOT_PRINT PrintBytecodeInfo(graph); #endif for (const auto &[key, value]: jsgateToBytecode_) { byteCodeToJSGate_[value.second] = key; } // ts loader panda::ecmascript::TSLoader* tsLoader = vm_->GetTSLoader(); // resolve def-site of virtual regs and set all value inputs for (auto gate: circuit_.GetAllGates()) { auto valueCount = circuit_.GetOpCode(gate).GetInValueCount(circuit_.GetBitField(gate)); auto it = jsgateToBytecode_.find(gate); if (it == jsgateToBytecode_.end()) { continue; } if (circuit_.LoadGatePtrConst(gate)->GetOpCode() == OpCode::CONSTANT) { continue; } const auto &[id, pc] = it->second; auto bytecodeInfo = GetBytecodeInfo(pc); [[maybe_unused]] size_t numValueInputs = (bytecodeInfo.accIn ? 1 : 0) + bytecodeInfo.inputs.size(); [[maybe_unused]] size_t numValueOutputs = (bytecodeInfo.accOut ? 1 : 0) + bytecodeInfo.vregOut.size(); ASSERT(numValueInputs == valueCount); ASSERT(numValueOutputs <= 1); // recursive variables renaming algorithm std::function defSiteOfReg = [&](size_t bbId, const uint8_t *end, uint16_t reg, bool acc) -> GateRef { // find def-site in bytecodes of basic block auto ans = Circuit::NullGate(); auto &bb = graph.at(bbId); std::vector instList; { auto pcIter = bb.start; while (pcIter <= end) { instList.push_back(pcIter); auto curInfo = GetBytecodeInfo(pcIter); pcIter += curInfo.offset; } } std::reverse(instList.begin(), instList.end()); for (auto pcIter: instList) { // upper bound auto curInfo = GetBytecodeInfo(pcIter); if (acc) { if (curInfo.accOut) { if (IsMov(static_cast(curInfo.opcode))) { acc = curInfo.accIn; if (!curInfo.inputs.empty()) { ASSERT(!acc); ASSERT(curInfo.inputs.size() == 1); reg = std::get(curInfo.inputs.at(0)).GetId(); } } else { ans = byteCodeToJSGate_.at(pcIter); break; } } } else { if (!curInfo.vregOut.empty() && curInfo.vregOut.at(0) == reg) { if (IsMov(static_cast(curInfo.opcode))) { acc = curInfo.accIn; if (!curInfo.inputs.empty()) { ASSERT(!acc); ASSERT(curInfo.inputs.size() == 1); reg = std::get(curInfo.inputs.at(0)).GetId(); } } else { ans = byteCodeToJSGate_.at(pcIter); break; } } } } // find GET_EXCEPTION gate if this is a catch block if (ans == Circuit::NullGate() && acc) { if (!bb.trys.empty()) { const auto &outList = circuit_.GetOutVector(bb.dependStart); ASSERT(outList.size() == 1); const auto &getExceptionGate = outList.at(0); ASSERT(circuit_.GetOpCode(getExceptionGate) == OpCode::GET_EXCEPTION); ans = getExceptionGate; } } // find def-site in value selectors of vregs if (ans == Circuit::NullGate() && !acc && bb.phi.count(reg)) { if (!bb.vregToValSelectorGate.count(reg)) { auto gate = circuit_.NewGate(OpCode(OpCode::VALUE_SELECTOR), MachineType::I64, bb.numOfStatePreds, std::vector( 1 + bb.numOfStatePreds, Circuit::NullGate()), GateType::JS_ANY); bb.vregToValSelectorGate[reg] = gate; circuit_.NewIn(gate, 0, bb.stateStart); for (int32_t i = 0; i < bb.numOfStatePreds; ++i) { auto &[predId, predPc, isException] = bb.expandedPreds.at(i); circuit_.NewIn(gate, i + 1, defSiteOfReg(predId, predPc, reg, acc)); } } ans = bb.vregToValSelectorGate.at(reg); } // find def-site in value selectors of acc if (ans == Circuit::NullGate() && acc && bb.phiAcc) { if (bb.valueSelectorAccGate == Circuit::NullGate()) { auto gate = circuit_.NewGate(OpCode(OpCode::VALUE_SELECTOR), MachineType::I64, bb.numOfStatePreds, std::vector( 1 + bb.numOfStatePreds, Circuit::NullGate()), GateType::JS_ANY); bb.valueSelectorAccGate = gate; circuit_.NewIn(gate, 0, bb.stateStart); for (int32_t i = 0; i < bb.numOfStatePreds; ++i) { auto &[predId, predPc, isException] = bb.expandedPreds.at(i); circuit_.NewIn(gate, i + 1, defSiteOfReg(predId, predPc, reg, acc)); } } ans = bb.valueSelectorAccGate; } if (ans == Circuit::NullGate() && bbId == 0) { // entry block // find def-site in function args ASSERT(!acc && reg >= offsetArgs && reg < offsetArgs + argGates.size()); const panda_file::File *pf = file_->GetPandaFile(); auto argVreg = reg - offsetArgs; auto tsType = tsLoader->GetGTFromPandaFile(*pf, argVreg, method_).GetGlobalTSTypeRef(); auto index = GetFunctionArgIndex(reg, offsetArgs); circuit_.LoadGatePtr(ans)->SetGateType(static_cast(tsType)); return argGates.at(index); } if (ans == Circuit::NullGate()) { // recursively find def-site in dominator block return defSiteOfReg(bb.iDominator->id, bb.iDominator->end, reg, acc); } else { // def-site already found const panda_file::File *pf = file_->GetPandaFile(); auto tsType = tsLoader->GetGTFromPandaFile(*pf, reg, method_).GetGlobalTSTypeRef(); circuit_.LoadGatePtr(ans)->SetGateType(static_cast(tsType)); return ans; } }; auto stateCount = circuit_.GetOpCode(gate).GetStateCount(circuit_.GetBitField(gate)); auto dependCount = circuit_.GetOpCode(gate).GetDependCount(circuit_.GetBitField(gate)); for (size_t valueIdx = 0; valueIdx < valueCount; valueIdx++) { auto inIdx = valueIdx + stateCount + dependCount; if (!circuit_.IsInGateNull(gate, inIdx)) { continue; } if (valueIdx < bytecodeInfo.inputs.size()) { circuit_.NewIn(gate, inIdx, defSiteOfReg(id, pc - 1, std::get(bytecodeInfo.inputs.at(valueIdx)).GetId(), false)); } else { circuit_.NewIn(gate, inIdx, defSiteOfReg(id, pc - 1, 0, true)); } } } #if ECMASCRIPT_ENABLE_TS_AOT_PRINT circuit_.PrintAllGates(*this); #endif } size_t BytecodeCircuitBuilder::GetFunctionArgIndex(size_t currentVreg, size_t numVregs) const { return (currentVreg - numVregs + CommonArgIdx::NUM_OF_ARGS); } void BytecodeCircuitBuilder::PrintCollectBlockInfo(std::vector &bytecodeBlockInfos) { for (auto iter = bytecodeBlockInfos.begin(); iter != bytecodeBlockInfos.end(); iter++) { std::cout << "offset: " << static_cast(iter->pc) << " splitKind: " << static_cast(iter->splitKind) << " successor are: "; auto &vec = iter->succs; for (size_t i = 0; i < vec.size(); i++) { std::cout << static_cast(vec[i]) << " , "; } std::cout << "" << std::endl; } std::cout << "-----------------------------------------------------------------------" << std::endl; } void BytecodeCircuitBuilder::PrintGraph(std::vector &graph) { for (size_t i = 0; i < graph.size(); i++) { if (graph[i].isDead) { std::cout << "BB_" << graph[i].id << ": ;predsId= invalid BB" << std::endl; std::cout << "curStartPc: " << static_cast(graph[i].start) << " curEndPc: " << static_cast(graph[i].end) << std::endl; continue; } std::cout << "BB_" << graph[i].id << ": ;predsId= "; for (size_t k = 0; k < graph[i].preds.size(); ++k) { std::cout << graph[i].preds[k]->id << ", "; } std::cout << "" << std::endl; std::cout << "curStartPc: " << static_cast(graph[i].start) << " curEndPc: " << static_cast(graph[i].end) << std::endl; for (size_t j = 0; j < graph[i].preds.size(); j++) { std::cout << "predsStartPc: " << static_cast(graph[i].preds[j]->start) << " predsEndPc: " << static_cast(graph[i].preds[j]->end) << std::endl; } for (size_t j = 0; j < graph[i].succs.size(); j++) { std::cout << "succesStartPc: " << static_cast(graph[i].succs[j]->start) << " succesEndPc: " << static_cast(graph[i].succs[j]->end) << std::endl; } std::cout << "succesId: "; for (size_t j = 0; j < graph[i].succs.size(); j++) { std::cout << graph[i].succs[j]->id << ", "; } std::cout << "" << std::endl; for (size_t j = 0; j < graph[i].catchs.size(); j++) { std::cout << "catchStartPc: " << static_cast(graph[i].catchs[j]->start) << " catchEndPc: " << static_cast(graph[i].catchs[j]->end) << std::endl; } for (size_t j = 0; j < graph[i].immDomBlocks.size(); j++) { std::cout << "dominate block id: " << graph[i].immDomBlocks[j]->id << " startPc: " << static_cast(graph[i].immDomBlocks[j]->start) << " endPc: " << static_cast(graph[i].immDomBlocks[j]->end) << std::endl; } if (graph[i].iDominator) { std::cout << "current block " << graph[i].id << " immediate dominator is " << graph[i].iDominator->id << std::endl; } std::cout << "current block " << graph[i].id << " dominance Frontiers: "; for (const auto &frontier: graph[i].domFrontiers) { std::cout << frontier->id << " , "; } std::cout << std::endl; std::cout << "current block " << graph[i].id << " phi variable: "; for (auto variable: graph[i].phi) { std::cout << variable << " , "; } std::cout << std::endl; std::cout << "-------------------------------------------------------" << std::endl; } } void BytecodeCircuitBuilder::PrintBytecodeInfo(std::vector &graph) { for (auto &bb: graph) { if (bb.isDead) { continue; } auto pc = bb.start; std::cout << "BB_" << bb.id << ": " << std::endl; while (pc <= bb.end) { auto curInfo = GetBytecodeInfo(pc); std::cout << "Inst_" << GetEcmaOpcodeStr(static_cast(*pc)) << ": "; std::cout << "In=["; if (curInfo.accIn) { std::cout << "acc" << ","; } for (const auto &in: curInfo.inputs) { if (std::holds_alternative(in)) { std::cout << std::get(in).GetId() << ","; } } std::cout << "] Out=["; if (curInfo.accOut) { std::cout << "acc" << ","; } for (const auto &out: curInfo.vregOut) { std::cout << out << ","; } std::cout << "]"; std::cout << std::endl; pc += curInfo.offset; } } } void BytecodeCircuitBuilder::PrintBBInfo(std::vector &graph) { for (auto &bb: graph) { if (bb.isDead) { continue; } std::cout << "------------------------" << std::endl; std::cout << "block: " << bb.id << std::endl; std::cout << "preds: "; for (auto pred: bb.preds) { std::cout << pred->id << " , "; } std::cout << std::endl; std::cout << "succs: "; for (auto succ: bb.succs) { std::cout << succ->id << " , "; } std::cout << std::endl; std::cout << "catchs: "; for (auto catchBlock: bb.catchs) { std::cout << catchBlock->id << " , "; } std::cout << std::endl; std::cout << "trys: "; for (auto tryBlock: bb.trys) { std::cout << tryBlock->id << " , "; } std::cout << std::endl; } } } // namespace panda::ecmascript::kungfu