/* * 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 "ecmascript/compiler/bytecode_circuit_builder.h" #include "ecmascript/base/number_helper.h" #include "ecmascript/compiler/gate_accessor.h" #include "ecmascript/ts_types/ts_manager.h" #include "libpandafile/bytecode_instruction-inl.h" namespace panda::ecmascript::kungfu { void BytecodeCircuitBuilder::BytecodeToCircuit() { ExceptionInfo exceptionInfo = {}; // collect try catch block info CollectTryCatchBlockInfo(exceptionInfo); hasTryCatch_ = exceptionInfo.size() != 0; BuildRegionInfo(); // Building the basic block diagram of bytecode BuildRegions(exceptionInfo); } void BytecodeCircuitBuilder::BuildRegionInfo() { uint32_t size = pcOffsets_.size(); uint32_t end = size - 1; // 1: end BytecodeIterator iterator(this, 0, end); infoData_.resize(size); byteCodeToJSGates_.resize(size, std::vector<GateRef>(0)); regionsInfo_.InsertHead(0); // 0: start pc iterator.GotoStart(); while (!iterator.Done()) { auto index = iterator.Index(); auto &info = infoData_[index]; auto pc = pcOffsets_[index]; info.metaData_ = bytecodes_->GetBytecodeMetaData(pc); ASSERT(!info.metaData_.IsInvalid()); BytecodeInfo::InitBytecodeInfo(this, info, pc); CollectRegionInfo(index); ++iterator; } } void BytecodeCircuitBuilder::CollectRegionInfo(uint32_t bcIndex) { auto pc = pcOffsets_[bcIndex]; auto &info = infoData_[bcIndex]; int32_t offset = 0; if (info.IsJump()) { switch (info.GetOpcode()) { case EcmaOpcode::JEQZ_IMM8: case EcmaOpcode::JNEZ_IMM8: case EcmaOpcode::JMP_IMM8: offset = static_cast<int8_t>(READ_INST_8_0()); break; case EcmaOpcode::JNEZ_IMM16: case EcmaOpcode::JEQZ_IMM16: case EcmaOpcode::JMP_IMM16: offset = static_cast<int16_t>(READ_INST_16_0()); break; case EcmaOpcode::JMP_IMM32: case EcmaOpcode::JNEZ_IMM32: case EcmaOpcode::JEQZ_IMM32: offset = static_cast<int32_t>(READ_INST_32_0()); break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); break; } auto nextIndex = bcIndex + 1; // 1: next pc auto targetIndex = FindBcIndexByPc(pc + offset); // condition branch current basic block end if (info.IsCondJump()) { regionsInfo_.InsertSplit(nextIndex); regionsInfo_.InsertJump(targetIndex, bcIndex, false); } else { if (bcIndex != GetLastBcIndex()) { regionsInfo_.InsertHead(nextIndex); } regionsInfo_.InsertJump(targetIndex, bcIndex, true); } } else if (info.IsReturn() || info.IsThrow()) { if (bcIndex != GetLastBcIndex()) { auto nextIndex = bcIndex + 1; // 1: next pc regionsInfo_.InsertHead(nextIndex); } } } void BytecodeCircuitBuilder::CollectTryCatchBlockInfo(ExceptionInfo &byteCodeException) { auto pf = file_->GetPandaFile(); panda_file::MethodDataAccessor mda(*pf, method_->GetMethodId()); panda_file::CodeDataAccessor cda(*pf, mda.GetCodeId().value()); cda.EnumerateTryBlocks([this, &byteCodeException]( panda_file::CodeDataAccessor::TryBlock &tryBlock) { auto tryStartOffset = tryBlock.GetStartPc(); auto tryEndOffset = tryBlock.GetStartPc() + tryBlock.GetLength(); auto tryStartPc = const_cast<uint8_t *>(method_->GetBytecodeArray() + tryStartOffset); auto tryEndPc = const_cast<uint8_t *>(method_->GetBytecodeArray() + tryEndOffset); // skip try blocks with same pc in start and end label if (tryStartPc == tryEndPc) { return true; } auto tryStartBcIndex = FindBcIndexByPc(tryStartPc); regionsInfo_.InsertSplit(tryStartBcIndex); if (tryEndPc <= GetLastPC()) { auto tryEndBcIndex = FindBcIndexByPc(tryEndPc); regionsInfo_.InsertSplit(tryEndBcIndex); } byteCodeException.emplace_back(ExceptionItem { tryStartPc, tryEndPc, {} }); tryBlock.EnumerateCatchBlocks([&](panda_file::CodeDataAccessor::CatchBlock &catchBlock) { auto pcOffset = catchBlock.GetHandlerPc(); auto catchBlockPc = const_cast<uint8_t *>(method_->GetBytecodeArray() + pcOffset); auto catchBlockBcIndex = FindBcIndexByPc(catchBlockPc); regionsInfo_.InsertHead(catchBlockBcIndex); // try block associate catch block byteCodeException.back().catches.emplace_back(catchBlockPc); return true; }); return true; }); } void BytecodeCircuitBuilder::BuildEntryBlock() { BytecodeRegion &entryBlock = RegionAt(0); BytecodeRegion &nextBlock = RegionAt(1); entryBlock.succs.emplace_back(&nextBlock); nextBlock.preds.emplace_back(&entryBlock); entryBlock.bytecodeIterator_.Reset(this, 0, BytecodeIterator::INVALID_INDEX); } void BytecodeCircuitBuilder::BuildRegions(const ExceptionInfo &byteCodeException) { auto &items = regionsInfo_.GetBlockItems(); auto blockSize = items.size(); // 1 : entry block. if the loop head is in the first bb block, the variables used in the head cannot correctly // generate Phi nodes through the dominator-tree algorithm, resulting in an infinite loop. Therefore, an empty // BB block is generated as an entry block graph_.resize(blockSize + 1, nullptr); for (size_t i = 0; i < graph_.size(); i++) { graph_[i] = circuit_->chunk()->New<BytecodeRegion>(circuit_->chunk()); } // build entry block BuildEntryBlock(); // build basic block size_t blockId = 1; for (const auto &item : items) { auto &curBlock = GetBasicBlockById(blockId); curBlock.id = blockId; curBlock.start = item.GetStartBcIndex(); if (blockId != 1) { auto &prevBlock = RegionAt(blockId - 1); prevBlock.end = curBlock.start - 1; prevBlock.bytecodeIterator_.Reset(this, prevBlock.start, prevBlock.end); // fall through if (!item.IsHeadBlock()) { curBlock.preds.emplace_back(&prevBlock); prevBlock.succs.emplace_back(&curBlock); } } blockId++; } auto &lastBlock = RegionAt(blockId - 1); // 1: last block lastBlock.end = GetLastBcIndex(); lastBlock.bytecodeIterator_.Reset(this, lastBlock.start, lastBlock.end); auto &splitItems = regionsInfo_.GetSplitItems(); for (const auto &item : splitItems) { auto curIndex = regionsInfo_.FindBBIndexByBcIndex(item.startBcIndex); auto &curBlock = GetBasicBlockById(curIndex); auto predIndex = regionsInfo_.FindBBIndexByBcIndex(item.predBcIndex); auto &predBlock = GetBasicBlockById(predIndex); curBlock.preds.emplace_back(&predBlock); predBlock.succs.emplace_back(&curBlock); } if (byteCodeException.size() != 0) { BuildCatchBlocks(byteCodeException); } UpdateCFG(); if (HasTryCatch()) { CollectTryPredsInfo(); } RemoveUnreachableRegion(); if (IsLogEnabled()) { PrintGraph("Update CFG"); } BuildCircuit(); } void BytecodeCircuitBuilder::BuildCatchBlocks(const ExceptionInfo &byteCodeException) { // try catch block associate for (size_t i = 0; i < graph_.size(); i++) { auto &bb = RegionAt(i); auto startIndex = bb.start; const auto pc = pcOffsets_[startIndex]; for (auto it = byteCodeException.cbegin(); it != byteCodeException.cend(); it++) { if (pc < it->startPc || pc >= it->endPc) { continue; } // try block interval const auto &catches = it->catches; // catches start pc for (size_t j = i + 1; j < graph_.size(); j++) { auto &catchBB = RegionAt(j); const auto catchStart = pcOffsets_[catchBB.start]; if (std::find(catches.cbegin(), catches.cend(), catchStart) != catches.cend()) { bb.catches.insert(bb.catches.cbegin(), &catchBB); bb.succs.emplace_back(&catchBB); catchBB.preds.emplace_back(&bb); } } } // When there are multiple catch blocks in the current block, the set of catch blocks // needs to be sorted to satisfy the order of execution of catch blocks. bb.SortCatches(); } } void BytecodeCircuitBuilder::CollectTryPredsInfo() { for (size_t i = 0; i < graph_.size(); i++) { auto &bb = RegionAt(i); if (bb.catches.empty()) { continue; } else if (bb.catches.size() > 1) { // 1: cache size for (auto it = bb.catches.begin() + 1; it != bb.catches.end();) { // 1: invalid catch bb bb.EraseThisBlock((*it)->trys); it = bb.catches.erase(it); } } EnumerateBlock(bb, [&bb](const BytecodeInfo &bytecodeInfo) -> bool { if (bytecodeInfo.IsGeneral()) { // if block which can throw exception has serval catchs block, // only the innermost catch block is useful ASSERT(bb.catches.size() == 1); // 1: cache size if (!bytecodeInfo.NoThrow()) { bb.catches.at(0)->numOfStatePreds++; } } return true; }); } } void BytecodeCircuitBuilder::RemoveUnusedPredsInfo(BytecodeRegion& bb) { EnumerateBlock(bb, [&bb](const BytecodeInfo &bytecodeInfo) -> bool { if (bytecodeInfo.IsGeneral()) { ASSERT(bb.catches.size() == 1); // 1: cache size if (!bytecodeInfo.NoThrow()) { bb.catches.at(0)->numOfStatePreds--; } } return true; }); } void BytecodeCircuitBuilder::ClearUnreachableRegion(ChunkVector<BytecodeRegion*>& pendingList) { auto bb = pendingList.back(); pendingList.pop_back(); for (auto it = bb->preds.begin(); it != bb->preds.end(); it++) { if ((*it)->numOfStatePreds != 0) { bb->EraseThisBlock((*it)->succs); } } for (auto it = bb->succs.begin(); it != bb->succs.end(); it++) { auto bbNext = *it; if (bbNext->numOfStatePreds != 0) { bb->EraseThisBlock(bbNext->preds); bbNext->numOfStatePreds--; if (bbNext->numOfStatePreds == 0) { pendingList.emplace_back(bbNext); } } } for (auto it = bb->trys.begin(); it != bb->trys.end(); it++) { if ((*it)->numOfStatePreds != 0) { bb->EraseThisBlock((*it)->catches); } } for (auto it = bb->catches.begin(); it != bb->catches.end(); it++) { auto bbNext = *it; if (bbNext->numOfStatePreds != 0) { RemoveUnusedPredsInfo(*bb); bb->EraseThisBlock(bbNext->trys); if (bbNext->numOfStatePreds == 0) { pendingList.emplace_back(bbNext); } } } bb->preds.clear(); bb->succs.clear(); bb->trys.clear(); bb->catches.clear(); numOfLiveBB_--; } void BytecodeCircuitBuilder::RemoveUnreachableRegion() { numOfLiveBB_ = graph_.size(); ChunkVector<BytecodeRegion*> pendingList(circuit_->chunk()); for (size_t i = 1; i < graph_.size(); i++) { // 1: skip entry bb auto &bb = RegionAt(i); if (bb.numOfStatePreds == 0) { pendingList.emplace_back(&bb); } } while (!pendingList.empty()) { ClearUnreachableRegion(pendingList); } } // Update CFG's predecessor, successor and try catch associations void BytecodeCircuitBuilder::UpdateCFG() { for (size_t i = 0; i < graph_.size(); i++) { auto &bb = RegionAt(i); bb.preds.clear(); bb.trys.clear(); ChunkVector<BytecodeRegion *> newSuccs(circuit_->chunk()); for (const auto &succ: bb.succs) { if (std::count(bb.catches.cbegin(), bb.catches.cend(), succ)) { continue; } newSuccs.emplace_back(succ); } bb.succs.clear(); bb.succs.insert(bb.succs.end(), newSuccs.begin(), newSuccs.end()); } for (size_t i = 0; i < graph_.size(); i++) { auto &bb = RegionAt(i); for (auto &succ: bb.succs) { succ->preds.emplace_back(&bb); succ->numOfStatePreds++; } for (auto &catchBlock: bb.catches) { catchBlock->trys.emplace_back(&bb); } } } // build circuit void BytecodeCircuitBuilder::BuildCircuitArgs() { argAcc_.NewCommonArg(CommonArgIdx::GLUE, MachineType::I64, GateType::NJSValue()); if (!method_->IsFastCall()) { argAcc_.NewCommonArg(CommonArgIdx::ACTUAL_ARGC, MachineType::I64, GateType::NJSValue()); auto funcIdx = static_cast<size_t>(CommonArgIdx::FUNC); const size_t actualNumArgs = argAcc_.GetActualNumArgs(); // new actual argument gates for (size_t argIdx = funcIdx; argIdx < actualNumArgs; argIdx++) { argAcc_.NewArg(argIdx); } } else { auto funcIdx = static_cast<size_t>(FastCallArgIdx::FUNC); size_t actualNumArgs = static_cast<size_t>(FastCallArgIdx::NUM_OF_ARGS) + method_->GetNumArgsWithCallField(); for (size_t argIdx = funcIdx; argIdx < actualNumArgs; argIdx++) { argAcc_.NewArg(argIdx); } } argAcc_.CollectArgs(); if (HasTypes()) { argAcc_.FillArgsGateType(&typeRecorder_); } BuildFrameArgs(); } void BytecodeCircuitBuilder::BuildFrameArgs() { UInt32PairAccessor accessor(0, 0); auto metaData = circuit_->FrameArgs(accessor.ToValue()); size_t numArgs = metaData->GetNumIns(); std::vector<GateRef> args(numArgs, Circuit::NullGate()); size_t idx = 0; args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::FUNC); args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::NEW_TARGET); args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::THIS_OBJECT); args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::ACTUAL_ARGC); args[idx++] = GetPreFrameArgs(); GateRef frameArgs = circuit_->NewGate(metaData, args); argAcc_.SetFrameArgs(frameArgs); } std::vector<GateRef> BytecodeCircuitBuilder::CreateGateInList( const BytecodeInfo &info, const GateMetaData *meta) { auto numValues = meta->GetNumIns(); const size_t length = meta->GetInValueStarts(); std::vector<GateRef> inList(numValues, Circuit::NullGate()); auto inputSize = info.inputs.size(); for (size_t i = 0; i < inputSize; i++) { auto &input = info.inputs[i]; if (std::holds_alternative<ConstDataId>(input)) { inList[i + length] = circuit_->GetConstantGate(MachineType::I64, std::get<ConstDataId>(input).GetId(), GateType::NJSValue()); } else if (std::holds_alternative<Immediate>(input)) { inList[i + length] = circuit_->GetConstantGate(MachineType::I64, std::get<Immediate>(input).GetValue(), GateType::NJSValue()); } else if (std::holds_alternative<ICSlotId>(input)) { inList[i + length] = circuit_->GetConstantGate(MachineType::I16, std::get<ICSlotId>(input).GetId(), GateType::NJSValue()); } else { ASSERT(std::holds_alternative<VirtualRegister>(input)); continue; } } if (info.AccIn()) { inputSize++; } if (meta->HasFrameState()) { inList[inputSize + length] = GetFrameArgs(); } return inList; } GateRef BytecodeCircuitBuilder::NewConst(const BytecodeInfo &info) { auto opcode = info.GetOpcode(); GateRef gate = 0; switch (opcode) { case EcmaOpcode::LDNAN: gate = circuit_->GetConstantGate(MachineType::I64, base::NumberHelper::GetNaN(), GateType::NumberType()); break; case EcmaOpcode::LDINFINITY: gate = circuit_->GetConstantGate(MachineType::I64, base::NumberHelper::GetPositiveInfinity(), GateType::NumberType()); break; case EcmaOpcode::LDUNDEFINED: gate = circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_UNDEFINED, GateType::UndefinedType()); break; case EcmaOpcode::LDNULL: gate = circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_NULL, GateType::NullType()); break; case EcmaOpcode::LDTRUE: gate = circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_TRUE, GateType::BooleanType()); break; case EcmaOpcode::LDFALSE: gate = circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_FALSE, GateType::BooleanType()); break; case EcmaOpcode::LDHOLE: gate = circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_HOLE, GateType::TaggedValue()); break; case EcmaOpcode::LDAI_IMM32: gate = circuit_->GetConstantGate(MachineType::I64, std::get<Immediate>(info.inputs[0]).ToJSTaggedValueInt(), GateType::IntType()); break; case EcmaOpcode::FLDAI_IMM64: gate = circuit_->GetConstantGate(MachineType::I64, std::get<Immediate>(info.inputs.at(0)).ToJSTaggedValueDouble(), GateType::DoubleType()); break; case EcmaOpcode::LDFUNCTION: gate = argAcc_.GetCommonArgGate(CommonArgIdx::FUNC); break; case EcmaOpcode::LDNEWTARGET: gate = argAcc_.GetCommonArgGate(CommonArgIdx::NEW_TARGET); break; case EcmaOpcode::LDTHIS: gate = argAcc_.GetCommonArgGate(CommonArgIdx::THIS_OBJECT); break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } return gate; } void BytecodeCircuitBuilder::MergeThrowGate(BytecodeRegion &bb, uint32_t bcIndex) { auto state = frameStateBuilder_.GetCurrentState(); auto depend = frameStateBuilder_.GetCurrentDepend(); if (!bb.catches.empty()) { auto ifSuccess = circuit_->NewGate(circuit_->IfSuccess(), {state}); auto ifException = circuit_->NewGate(circuit_->IfException(), {state, depend}); frameStateBuilder_.UpdateStateDepend(ifException, ifException); ASSERT(bb.catches.size() == 1); // 1: one catch auto bbNext = bb.catches.at(0); frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext); bbNext->expandedPreds.push_back({bb.id, bcIndex, true}); state = ifSuccess; } auto constant = circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_EXCEPTION, GateType::TaggedValue()); circuit_->NewGate(circuit_->Return(), { state, depend, constant, circuit_->GetReturnRoot() }); } void BytecodeCircuitBuilder::MergeExceptionGete(BytecodeRegion &bb, const BytecodeInfo& bytecodeInfo, uint32_t bcIndex) { auto state = frameStateBuilder_.GetCurrentState(); auto depend = frameStateBuilder_.GetCurrentDepend(); auto ifSuccess = circuit_->NewGate(circuit_->IfSuccess(), {state}); ASSERT(bb.catches.size() == 1); // 1: one catch auto bbNext = bb.catches.at(0); auto ifException = circuit_->NewGate(circuit_->IfException(), {state, depend}); frameStateBuilder_.UpdateStateDepend(ifException, ifException); frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext); if (bytecodeInfo.GetOpcode() == EcmaOpcode::CREATEASYNCGENERATOROBJ_V8) { bbNext->expandedPreds.push_back({bb.id, bcIndex + 1, true}); // 1: next pc } else { bbNext->expandedPreds.push_back({bb.id, bcIndex, true}); } frameStateBuilder_.UpdateStateDepend(ifSuccess, depend); } void BytecodeCircuitBuilder::NewJSGate(BytecodeRegion &bb) { auto &iterator = bb.GetBytecodeIterator(); const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo(); GateRef state = frameStateBuilder_.GetCurrentState(); GateRef depend = frameStateBuilder_.GetCurrentDepend(); size_t numValueInputs = bytecodeInfo.ComputeValueInputCount(); GateRef gate = 0; bool writable = !bytecodeInfo.NoSideEffects(); bool hasFrameState = bytecodeInfo.HasFrameState(); size_t pcOffset = GetPcOffset(iterator.Index()); auto methodOffset = method_->GetMethodId().GetOffset(); auto meta = circuit_->JSBytecode( numValueInputs, methodOffset, bytecodeInfo.GetOpcode(), pcOffset, iterator.Index(), writable, hasFrameState); std::vector<GateRef> inList = CreateGateInList(bytecodeInfo, meta); if (bytecodeInfo.IsDef()) { gate = circuit_->NewGate(meta, MachineType::I64, inList.size(), inList.data(), GateType::AnyType()); } else { gate = circuit_->NewGate(meta, MachineType::NOVALUE, inList.size(), inList.data(), GateType::Empty()); } byteCodeToJSGates_[iterator.Index()].emplace_back(gate); jsGatesToByteCode_[gate] = iterator.Index(); gateAcc_.NewIn(gate, 0, state); gateAcc_.NewIn(gate, 1, depend); frameStateBuilder_.UpdateStateDepend(gate, gate); frameStateBuilder_.UpdateFrameValues(bytecodeInfo, iterator.Index(), gate); if (bytecodeInfo.IsThrow()) { MergeThrowGate(bb, iterator.Index()); return; } if (!bb.catches.empty() && !bytecodeInfo.NoThrow()) { MergeExceptionGete(bb, bytecodeInfo, iterator.Index()); } if (bytecodeInfo.IsGeneratorRelative()) { suspendAndResumeGates_.emplace_back(gate); } } void BytecodeCircuitBuilder::NewJump(BytecodeRegion &bb) { auto &iterator = bb.GetBytecodeIterator(); const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo(); GateRef state = frameStateBuilder_.GetCurrentState(); GateRef depend = frameStateBuilder_.GetCurrentDepend(); size_t numValueInputs = bytecodeInfo.ComputeValueInputCount(); if (bytecodeInfo.IsCondJump() && bb.succs.size() == 2) { // 2: two succ size_t pcOffset = GetPcOffset(iterator.Index()); auto methodOffset = method_->GetMethodId().GetOffset(); auto meta = circuit_->JSBytecode( numValueInputs, methodOffset, bytecodeInfo.GetOpcode(), pcOffset, iterator.Index(), false, false); auto numValues = meta->GetNumIns(); GateRef gate = circuit_->NewGate(meta, std::vector<GateRef>(numValues, Circuit::NullGate())); gateAcc_.NewIn(gate, 0, state); gateAcc_.NewIn(gate, 1, depend); frameStateBuilder_.UpdateStateDepend(gate, gate); frameStateBuilder_.UpdateFrameValues(bytecodeInfo, iterator.Index(), gate); auto ifTrue = circuit_->NewGate(circuit_->IfTrue(), {gate}); auto trueRelay = circuit_->NewGate(circuit_->DependRelay(), {ifTrue, gate}); auto ifFalse = circuit_->NewGate(circuit_->IfFalse(), {gate}); auto falseRelay = circuit_->NewGate(circuit_->DependRelay(), {ifFalse, gate}); for (auto &bbNext: bb.succs) { if (iterator.Index() + 1 == bbNext->start) { frameStateBuilder_.UpdateStateDepend(ifFalse, falseRelay); frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext); bbNext->expandedPreds.push_back({bb.id, iterator.Index(), false}); } else { frameStateBuilder_.UpdateStateDepend(ifTrue, trueRelay); frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext); bbNext->expandedPreds.push_back({bb.id, iterator.Index(), false}); } } byteCodeToJSGates_[iterator.Index()].emplace_back(gate); jsGatesToByteCode_[gate] = iterator.Index(); } else { ASSERT(bb.succs.size() == 1); auto &bbNext = bb.succs.at(0); frameStateBuilder_.MergeIntoSuccessor(bb, *bbNext); bbNext->expandedPreds.push_back({bb.id, iterator.Index(), false}); } } GateRef BytecodeCircuitBuilder::NewReturn(BytecodeRegion &bb) { ASSERT(bb.succs.empty()); auto &iterator = bb.GetBytecodeIterator(); const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo(); GateRef state = frameStateBuilder_.GetCurrentState(); GateRef depend = frameStateBuilder_.GetCurrentDepend(); GateRef gate = Circuit::NullGate(); if (bytecodeInfo.GetOpcode() == EcmaOpcode::RETURN) { // handle return.dyn bytecode gate = circuit_->NewGate(circuit_->Return(), { state, depend, Circuit::NullGate(), circuit_->GetReturnRoot() }); byteCodeToJSGates_[iterator.Index()].emplace_back(gate); jsGatesToByteCode_[gate] = iterator.Index(); } else if (bytecodeInfo.GetOpcode() == EcmaOpcode::RETURNUNDEFINED) { // handle returnundefined bytecode auto constant = circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_UNDEFINED, GateType::TaggedValue()); gate = circuit_->NewGate(circuit_->Return(), { state, depend, constant, circuit_->GetReturnRoot() }); byteCodeToJSGates_[iterator.Index()].emplace_back(gate); jsGatesToByteCode_[gate] = iterator.Index(); } return gate; } void BytecodeCircuitBuilder::NewByteCode(BytecodeRegion &bb) { auto &iterator = bb.GetBytecodeIterator(); const BytecodeInfo& bytecodeInfo = iterator.GetBytecodeInfo(); FrameLiveOut* liveout; auto bcId = iterator.Index(); if (iterator.IsInRange(bcId - 1)) { liveout = frameStateBuilder_.GetOrOCreateBCEndLiveOut(bcId - 1); } else { liveout = frameStateBuilder_.GetOrOCreateBBLiveOut(bb.id); } frameStateBuilder_.AdvanceToNextBc(bytecodeInfo, liveout, bcId); GateRef gate = Circuit::NullGate(); if (bytecodeInfo.IsSetConstant()) { // handle bytecode command to get constants gate = NewConst(bytecodeInfo); byteCodeToJSGates_[iterator.Index()].emplace_back(gate); jsGatesToByteCode_[gate] = iterator.Index(); } else if (bytecodeInfo.IsGeneral()) { // handle general ecma.* bytecodes NewJSGate(bb); } else if (bytecodeInfo.IsJump()) { // handle conditional jump and unconditional jump bytecodes NewJump(bb); } else if (bytecodeInfo.IsReturn()) { // handle return.dyn and returnundefined bytecodes gate = NewReturn(bb); } else if (bytecodeInfo.IsMov()) { frameStateBuilder_.UpdateMoveValues(bytecodeInfo, iterator.Index()); } else if (!bytecodeInfo.IsDiscarded()) { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } if (gate != Circuit::NullGate()) { frameStateBuilder_.UpdateFrameValues(bytecodeInfo, iterator.Index(), gate); } } void BytecodeCircuitBuilder::BuildSubCircuit() { auto &entryBlock = RegionAt(0); frameStateBuilder_.InitEntryBB(entryBlock); auto& rpoList = frameStateBuilder_.GetRpoList(); for (auto &bbId: rpoList) { auto &bb = RegionAt(bbId); frameStateBuilder_.AdvanceToNextBB(bb); if (IsEntryBlock(bb.id)) { if (NeedCheckSafePointAndStackOver()) { GateRef state = frameStateBuilder_.GetCurrentState(); GateRef depend = frameStateBuilder_.GetCurrentDepend(); auto stackCheck = circuit_->NewGate(circuit_->CheckSafePointAndStackOver(), {state, depend}); bb.dependCache = stackCheck; frameStateBuilder_.UpdateStateDepend(stackCheck, stackCheck); } auto &bbNext = RegionAt(bb.id + 1); frameStateBuilder_.MergeIntoSuccessor(bb, bbNext); bbNext.expandedPreds.push_back({bb.id, bb.end, false}); continue; } if (!bb.trys.empty()) { GateRef state = frameStateBuilder_.GetCurrentState(); GateRef depend = frameStateBuilder_.GetCurrentDepend(); auto getException = circuit_->NewGate(circuit_->GetException(), MachineType::I64, {state, depend}, GateType::AnyType()); frameStateBuilder_.UpdateAccumulator(getException); frameStateBuilder_.UpdateStateDepend(state, getException); } EnumerateBlock(bb, [this, &bb] (const BytecodeInfo &bytecodeInfo) -> bool { NewByteCode(bb); if (bytecodeInfo.IsJump() || bytecodeInfo.IsThrow()) { return false; } return true; }); bool needFallThrough = true; if (!bb.IsEmptryBlock()) { const BytecodeInfo& bytecodeInfo = GetBytecodeInfo(bb.end); needFallThrough = bytecodeInfo.needFallThrough(); } // fallThrough or empty merge bb if (needFallThrough) { ASSERT(bb.succs.size() == 1); // 1: fall through auto &bbNext = RegionAt(bb.succs[0]->id); frameStateBuilder_.MergeIntoSuccessor(bb, bbNext); bbNext.expandedPreds.push_back({bb.id, bb.end, false}); } } } void BytecodeCircuitBuilder::BuildCircuit() { // create arg gates array BuildCircuitArgs(); frameStateBuilder_.DoBytecodeAnalysis(); // build states sub-circuit of each block BuildSubCircuit(); if (IsLogEnabled()) { PrintGraph("Bytecode2Gate"); LOG_COMPILER(INFO) << "\033[34m" << "============= " << "After bytecode2circuit lowering [" << methodName_ << "]" << " =============" << "\033[0m"; circuit_->PrintAllGatesWithBytecode(); LOG_COMPILER(INFO) << "\033[34m" << "=========================== End ===========================" << "\033[0m"; } } void BytecodeCircuitBuilder::PrintGraph(const char* title) { LOG_COMPILER(INFO) << "======================== " << title << " ========================"; for (size_t i = 0; i < graph_.size(); i++) { BytecodeRegion& bb = RegionAt(i); if (!IsEntryBlock(bb.id) && bb.numOfStatePreds == 0) { LOG_COMPILER(INFO) << "B" << bb.id << ": ;preds= invalid BB"; LOG_COMPILER(INFO) << "\tBytecodePC: [" << std::to_string(bb.start) << ", " << std::to_string(bb.end) << ")"; continue; } std::string log("B" + std::to_string(bb.id) + ": ;preds= "); for (size_t k = 0; k < bb.preds.size(); ++k) { log += std::to_string(bb.preds[k]->id) + ", "; } LOG_COMPILER(INFO) << log; if (IsEntryBlock(bb.id)) { LOG_COMPILER(INFO) << "\tBytecodePC: Empty"; } else { LOG_COMPILER(INFO) << "\tBytecodePC: [" << std::to_string(bb.start) << ", " << std::to_string(bb.end) << ")"; } std::string log1("\tSucces: "); for (size_t j = 0; j < bb.succs.size(); j++) { log1 += std::to_string(bb.succs[j]->id) + ", "; } LOG_COMPILER(INFO) << log1; for (size_t j = 0; j < bb.catches.size(); j++) { LOG_COMPILER(INFO) << "\tcatch [: " << std::to_string(bb.catches[j]->start) << ", " << std::to_string(bb.catches[j]->end) << ")"; } std::string log2("\tTrys: "); for (auto tryBlock: bb.trys) { log2 += std::to_string(tryBlock->id) + " , "; } LOG_COMPILER(INFO) << log2; PrintBytecodeInfo(bb); LOG_COMPILER(INFO) << ""; } } void BytecodeCircuitBuilder::PrintBytecodeInfo(BytecodeRegion& bb) { if (IsEntryBlock(bb.id)) { LOG_COMPILER(INFO) << "\tBytecode[] = Empty"; return; } LOG_COMPILER(INFO) << "\tBytecode[] = "; EnumerateBlock(bb, [&](const BytecodeInfo &bytecodeInfo) -> bool { auto &iterator = bb.GetBytecodeIterator(); std::string log; log += std::string("\t\t< ") + std::to_string(iterator.Index()) + ": "; log += GetEcmaOpcodeStr(iterator.GetBytecodeInfo().GetOpcode()) + ", " + "In=["; if (bytecodeInfo.AccIn()) { log += "acc,"; } for (const auto &in: bytecodeInfo.inputs) { if (std::holds_alternative<VirtualRegister>(in)) { log += std::to_string(std::get<VirtualRegister>(in).GetId()) + ","; } } log += "], Out=["; if (bytecodeInfo.AccOut()) { log += "acc,"; } for (const auto &out: bytecodeInfo.vregOut) { log += std::to_string(out) + ","; } log += "] >"; LOG_COMPILER(INFO) << log; auto gate = GetGateByBcIndex(iterator.Index()); if (gate != Circuit::NullGate()) { this->gateAcc_.ShortPrint(gate); } return true; }); } } // namespace panda::ecmascript::kungfu