1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ecmascript/pgo_profiler/pgo_profiler.h" 17 18#include <chrono> 19#include <memory> 20 21#include "ecmascript/enum_conversion.h" 22#include "ecmascript/interpreter/interpreter-inl.h" 23#include "ecmascript/jit/jit_profiler.h" 24#include "ecmascript/pgo_profiler/pgo_profiler_info.h" 25#include "ecmascript/pgo_profiler/pgo_trace.h" 26 27namespace panda::ecmascript::pgo { 28void PGOProfiler::RecordProfileType(JSHClass *hclass, JSPandaFile *pandaFile, int32_t traceId) 29{ 30 if (!isEnable_) { 31 return; 32 } 33 ProfileType traceType = GetProfileType(hclass); 34 if (traceType.IsNone()) { 35 pgo::ApEntityId abcId(0); 36 pgo::PGOProfilerManager::GetInstance()->GetPandaFileId(pandaFile->GetJSPandaFileDesc(), abcId); 37 SetRootProfileType(hclass, abcId, traceId, ProfileType::Kind::ObjectLiteralId); 38 } 39} 40 41void PGOProfiler::ProfileDefineClass(JSTaggedType ctor) 42{ 43 if (!isEnable_) { 44 return; 45 } 46 auto ctorValue = JSTaggedValue(ctor); 47 if (!ctorValue.IsJSFunction()) { 48 return; 49 } 50 auto ctorFunc = JSFunction::Cast(ctorValue.GetTaggedObject()); 51 auto ctorMethodValue = ctorFunc->GetMethod(); 52 if (!ctorMethodValue.IsMethod()) { 53 return; 54 } 55 auto ctorMethod = Method::Cast(ctorMethodValue); 56 auto entityId = ctorMethod->GetMethodId().GetOffset(); 57 if (!InsertDefinedCtor(entityId)) { 58 InsertSkipCtorMethodIdSafe(ctorMethod->GetMethodId()); 59 return; 60 } 61 62 auto abcId = GetMethodAbcId(ctorFunc); 63 auto chc = ctorFunc->GetClass(); 64 SetRootProfileType(chc, abcId, entityId, ProfileType::Kind::ConstructorId); 65 66 auto protoOrHClass = ctorFunc->GetProtoOrHClass(); 67 if (protoOrHClass.IsJSHClass()) { 68 auto ihc = JSHClass::Cast(protoOrHClass.GetTaggedObject()); 69 SetRootProfileType(ihc, abcId, entityId, ProfileType::Kind::ClassId); 70 protoOrHClass = ihc->GetProto(); 71 } 72 if (protoOrHClass.IsJSObject()) { 73 auto phc = protoOrHClass.GetTaggedObject()->GetClass(); 74 SetRootProfileType(phc, abcId, entityId, ProfileType::Kind::PrototypeId); 75 } 76} 77 78void PGOProfiler::ProfileClassRootHClass(JSTaggedType ctor, JSTaggedType rootHcValue, ProfileType::Kind kind) 79{ 80 if (!isEnable_) { 81 return; 82 } 83 84 auto ctorValue = JSTaggedValue(ctor); 85 if (!ctorValue.IsJSFunction()) { 86 return; 87 } 88 auto ctorFunc = JSFunction::Cast(ctorValue.GetTaggedObject()); 89 if (!FunctionKindVerify(ctorFunc)) { 90 return; 91 } 92 auto ctorMethodValue = ctorFunc->GetMethod(); 93 if (!ctorMethodValue.IsMethod()) { 94 return; 95 } 96 auto ctorMethod = Method::Cast(ctorMethodValue); 97 auto entityId = ctorMethod->GetMethodId().GetOffset(); 98 if (IsSkippableCtor(entityId)) { 99 return; 100 } 101 102 auto rootHc = JSHClass::Cast(JSTaggedValue(rootHcValue).GetTaggedObject()); 103 auto abcId = GetMethodAbcId(ctorFunc); 104 SetRootProfileType(rootHc, abcId, entityId, kind); 105} 106 107void PGOProfiler::ProfileProtoTransitionClass(JSHandle<JSFunction> func, 108 JSHandle<JSHClass> hclass, 109 JSHandle<JSTaggedValue> proto) 110{ 111 if (!isEnable_) { 112 return; 113 } 114 auto thread = vm_->GetJSThread(); 115 JSHClass *phc = proto->GetTaggedObject()->GetClass(); 116 JSHClass *phcRoot = JSHClass::FindRootHClass(phc); 117 auto *transitionTable = thread->GetCurrentEcmaContext()->GetFunctionProtoTransitionTable(); 118 JSTaggedType baseIhc = transitionTable->GetFakeParent(JSTaggedType(phcRoot)); 119 if (baseIhc == 0) { 120 LOG_ECMA(DEBUG) << "fake parent not found!"; 121 ProfileClassRootHClass(func.GetTaggedType(), hclass.GetTaggedType()); 122 return; 123 } 124 JSTaggedType ihc = func->GetProtoTransRootHClass().GetRawData(); 125 if (JSTaggedValue(ihc).IsUndefined()) { 126 LOG_ECMA(DEBUG) << "maybe the prototype of the current function is just the initial prototype!"; 127 ProfileClassRootHClass(func.GetTaggedType(), hclass.GetTaggedType()); 128 return; 129 } 130 [[maybe_unused]] bool success = transitionTable->TryInsertFakeParentItem(hclass.GetTaggedType(), ihc); 131 ASSERT(success == true); // ihc wont conflict 132 // record original ihc type 133 ProfileClassRootHClass(func.GetTaggedType(), ihc, ProfileType::Kind::ClassId); 134 // record transition ihc type 135 ProfileClassRootHClass(func.GetTaggedType(), hclass.GetTaggedType(), ProfileType::Kind::TransitionClassId); 136} 137 138void PGOProfiler::ProfileProtoTransitionPrototype(JSHandle<JSFunction> func, 139 JSHandle<JSTaggedValue> prototype, 140 JSHandle<JSTaggedValue> oldPrototype, 141 JSHandle<JSTaggedValue> baseIhc) 142{ 143 if (!isEnable_) { 144 return; 145 } 146 147 // fuzz test modifies prototype explicitly, add check protection 148 if (!oldPrototype->IsECMAObject()) { 149 return; 150 } 151 152 auto method = func->GetMethod(); 153 if (Method::Cast(method)->IsNativeWithCallField()) { 154 return; 155 } 156 // set prototype once, and just skip this time 157 if (!func->GetProtoTransRootHClass().IsUndefined()) { 158 return; 159 } 160 auto thread = vm_->GetJSThread(); 161 // insert transition item 162 JSHandle<JSTaggedValue> transIhc(thread, JSTaggedValue::Undefined()); 163 JSHandle<JSTaggedValue> transPhc(thread, prototype->GetTaggedObject()->GetClass()); 164 if (JSHandle<JSHClass>(baseIhc)->IsDictionaryMode() || JSHandle<JSHClass>(transPhc)->IsDictionaryMode()) { 165 return; 166 } 167 auto *transitionTable = thread->GetCurrentEcmaContext()->GetFunctionProtoTransitionTable(); 168 bool success = transitionTable->TryInsertFakeParentItem(transPhc.GetTaggedType(), baseIhc.GetTaggedType()); 169 if (!success) { 170 return; 171 } 172 // Do not generate ihc lazily, beacause it's used for the key of hash table 173 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 174 JSHandle<JSHClass> ihc = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, oldPrototype); 175 func->SetProtoTransRootHClass(thread, JSHandle<JSTaggedValue>(ihc)); 176 177 // record phc type 178 JSHClass *phc0Root = JSHClass::FindRootHClass(oldPrototype->GetTaggedObject()->GetClass()); 179 ProfileClassRootHClass(func.GetTaggedType(), JSTaggedType(phc0Root), ProfileType::Kind::PrototypeId); 180 ProfileClassRootHClass(func.GetTaggedType(), transPhc.GetTaggedType(), ProfileType::Kind::TransitionPrototypeId); 181} 182 183void PGOProfiler::ProfileDefineGetterSetter(JSHClass* receiverHClass, 184 JSHClass* holderHClass, 185 const JSHandle<JSTaggedValue>& func, 186 int32_t pcOffset) 187{ 188 if (!isEnable_) { 189 return; 190 } 191 JSTaggedValue funcValue = JSTaggedValue(func.GetTaggedValue()); 192 if (!funcValue.IsJSFunction()) { 193 return; 194 } 195 auto methodValue = JSFunction::Cast(funcValue)->GetMethod(); 196 if (!methodValue.IsMethod()) { 197 return; 198 } 199 200 JSHandle<JSFunction> function(func); 201 202 WorkNode* workNode = reinterpret_cast<WorkNode*>(function->GetWorkNodePointer()); 203 if (workNode != nullptr) { 204 workNode->SetValue(JSTaggedType(JSFunction::Cast(funcValue))); 205 } 206 207 JSHandle<JSTaggedValue> key(vm_->GetJSThread(), JSTaggedValue(pcOffset)); 208 JSHandle<JSTaggedValue> receiverHClassHandle(vm_->GetJSThread(), receiverHClass); 209 JSHandle<JSTaggedValue> holderHClassHandle(vm_->GetJSThread(), holderHClass); 210 JSHandle<JSTaggedValue> profileTypeInfoValue(vm_->GetJSThread(), function->GetRawProfileTypeInfo()); 211 JSHandle<ProfileTypeInfoCell> profileTypeInfoCell(profileTypeInfoValue); 212 213 if (function->HasProfileTypeInfo(vm_->GetJSThread())) { 214 JSHandle<ProfileTypeInfo> profileTypeInfo(vm_->GetJSThread(), profileTypeInfoCell->GetValue()); 215 JSHandle<NumberDictionary> dictJShandle = ProfileTypeInfo::CreateOrGetExtraInfoMap(vm_->GetJSThread(), 216 profileTypeInfo); 217 int entry = dictJShandle->FindEntry(key.GetTaggedValue()); 218 if (entry == -1) { 219 ProfileTypeInfo::UpdateExtraInfoMap(vm_->GetJSThread(), dictJShandle, key, receiverHClassHandle, 220 holderHClassHandle, profileTypeInfo); 221 return; 222 } 223 ExtraProfileTypeInfo *mapInfoObj = ExtraProfileTypeInfo::Cast(dictJShandle->GetValue(entry).GetTaggedObject()); 224 if (mapInfoObj->GetReceiver() == receiverHClassHandle.GetTaggedValue().CreateAndGetWeakRef() && 225 mapInfoObj->GetHolder() == holderHClassHandle.GetTaggedValue()) { 226 return; 227 } 228 229 ExtraProfileTypeInfo::Cast(dictJShandle->GetValue(entry).GetTaggedObject())->Clear(vm_->GetJSThread()); 230 } 231} 232 233void PGOProfiler::UpdateRootProfileTypeSafe(JSHClass* oldHClass, JSHClass* newHClass) 234{ 235 if (!isEnable_) { 236 return; 237 } 238 ProfileType oldPt = GetProfileType(oldHClass); 239 if (oldPt.IsRootType()) { 240 newHClass->SetProfileType(oldPt.GetRaw()); 241 oldHClass->SetProfileType(0); 242 } 243} 244 245void PGOProfiler::UpdateTrackElementsKind(JSTaggedValue trackInfoVal, ElementsKind newKind) 246{ 247 if (trackInfoVal.IsHeapObject() && trackInfoVal.IsWeak()) { 248 auto trackInfo = TrackInfo::Cast(trackInfoVal.GetWeakReferentUnChecked()); 249 auto oldKind = trackInfo->GetElementsKind(); 250 if (Elements::IsGeneric(oldKind) || oldKind == newKind) { 251 return; 252 } 253 auto mixKind = Elements::MergeElementsKind(oldKind, newKind); 254 if (mixKind == oldKind) { 255 return; 256 } 257 trackInfo->SetElementsKind(mixKind); 258 auto thread = vm_->GetJSThread(); 259 auto globalConst = thread->GlobalConstants(); 260 // Since trackinfo is only used at define point, 261 // we update cachedHClass with initial array hclass which does not have IsPrototype set. 262 auto constantId = thread->GetArrayHClassIndexMap().at(mixKind).first; 263 auto hclass = globalConst->GetGlobalConstantObject(static_cast<size_t>(constantId)); 264 trackInfo->SetCachedHClass(vm_->GetJSThread(), hclass); 265 UpdateTrackInfo(JSTaggedValue(trackInfo)); 266 } 267} 268 269void PGOProfiler::UpdateTrackArrayLength(JSTaggedValue trackInfoVal, uint32_t newSize) 270{ 271 if (trackInfoVal.IsHeapObject() && trackInfoVal.IsWeak()) { 272 auto trackInfo = TrackInfo::Cast(trackInfoVal.GetWeakReferentUnChecked()); 273 uint32_t oldSize = trackInfo->GetArrayLength(); 274 if (oldSize >= newSize) { 275 return; 276 } 277 trackInfo->SetArrayLength(newSize); 278 UpdateTrackInfo(JSTaggedValue(trackInfo)); 279 } 280} 281 282void PGOProfiler::UpdateTrackSpaceFlag(TaggedObject *object, RegionSpaceFlag spaceFlag) 283{ 284 if (!object->GetClass()->IsTrackInfoObject()) { 285 return; 286 } 287 auto trackInfo = TrackInfo::Cast(object); 288 RegionSpaceFlag oldFlag = trackInfo->GetSpaceFlag(); 289 if (oldFlag == RegionSpaceFlag::IN_YOUNG_SPACE) { 290 trackInfo->SetSpaceFlag(spaceFlag); 291 UpdateTrackInfo(JSTaggedValue(trackInfo)); 292 } 293} 294 295void PGOProfiler::UpdateTrackInfo(JSTaggedValue trackInfoVal) 296{ 297 if (trackInfoVal.IsHeapObject()) { 298 auto trackInfo = TrackInfo::Cast(trackInfoVal.GetTaggedObject()); 299 auto func = trackInfo->GetCachedFunc(); 300 auto thread = vm_->GetJSThread(); 301 if (!func.IsWeak()) { 302 return; 303 } 304 TaggedObject *object = func.GetWeakReferentUnChecked(); 305 if (!object->GetClass()->IsJSFunction()) { 306 return; 307 } 308 JSFunction* function = JSFunction::Cast(object); 309 if (!function->HasProfileTypeInfo(thread)) { 310 return; 311 } 312 auto profileTypeInfoVal = function->GetProfileTypeInfo(); 313 if (profileTypeInfoVal.IsUndefined() || !profileTypeInfoVal.IsTaggedArray()) { 314 return; 315 } 316 auto profileTypeInfo = ProfileTypeInfo::Cast(profileTypeInfoVal.GetTaggedObject()); 317 if (profileTypeInfo->IsProfileTypeInfoWithBigMethod()) { 318 return; 319 } 320 if (!profileTypeInfo->IsProfileTypeInfoPreDumped()) { 321 profileTypeInfo->SetPreDumpPeriodIndex(); 322 PGOPreDump(JSTaggedType(object)); 323 } 324 } 325} 326 327void PGOProfiler::PGODump(JSTaggedType func) 328{ 329 if (!isEnable_ || !vm_->GetJSOptions().IsEnableProfileDump()) { 330 return; 331 } 332 333 auto funcValue = JSTaggedValue(func); 334 if (!funcValue.IsJSFunction()) { 335 return; 336 } 337 auto methodValue = JSFunction::Cast(funcValue)->GetMethod(); 338 if (!methodValue.IsMethod()) { 339 return; 340 } 341 auto function = JSFunction::Cast(funcValue); 342 auto workNode = reinterpret_cast<WorkNode *>(function->GetWorkNodePointer()); 343 if (workNode == nullptr) { 344 workNode = nativeAreaAllocator_->New<WorkNode>(JSTaggedType(function)); 345 function->SetWorkNodePointer(reinterpret_cast<uintptr_t>(workNode)); 346 LockHolder lock(mutex_); 347 dumpWorkList_.PushBack(workNode); 348 } else { 349 workNode->SetValue(JSTaggedType(function)); 350 auto workList = workNode->GetWorkList(); 351 LockHolder lock(mutex_); 352 if (workList == &preDumpWorkList_) { 353 preDumpWorkList_.Remove(workNode); 354 } 355 if (workList != &dumpWorkList_) { 356 dumpWorkList_.PushBack(workNode); 357 } 358 } 359 StartPGODump(); 360} 361 362void PGOProfiler::SuspendByGC() 363{ 364 if (!isEnable_) { 365 return; 366 } 367 LockHolder lock(mutex_); 368 if (GetState() == State::START) { 369 SetState(State::PAUSE); 370 WaitingPGODump(); 371 } else if (GetState() == State::FORCE_SAVE) { 372 SetState(State::FORCE_SAVE_PAUSE); 373 WaitingPGODump(); 374 } 375} 376 377void PGOProfiler::ResumeByGC() 378{ 379 if (!isEnable_) { 380 return; 381 } 382 LockHolder lock(mutex_); 383 if (GetState() == State::PAUSE) { 384 SetState(State::START); 385 DispatchPGODumpTask(); 386 } else if (GetState() == State::FORCE_SAVE_PAUSE) { 387 SetState(State::FORCE_SAVE); 388 DispatchPGODumpTask(); 389 } 390} 391 392void PGOProfiler::StopPGODump() 393{ 394 LockHolder lock(mutex_); 395 if (IsGCWaiting()) { 396 NotifyGC("[StopPGODump::PAUSE]"); 397 return; 398 } 399 SetState(State::STOP); 400 NotifyAll("[StopPGODump::STOP]"); 401} 402 403void PGOProfiler::StartPGODump() 404{ 405 if (GetState() == State::STOP) { 406 SetState(State::START); 407 DispatchPGODumpTask(); 408 } 409} 410 411void PGOProfiler::DispatchPGODumpTask() 412{ 413 Taskpool::GetCurrentTaskpool()->PostTask( 414 std::make_unique<PGOProfilerTask>(this, vm_->GetJSThread()->GetThreadId())); 415} 416 417PGOProfiler::State PGOProfiler::GetState() 418{ 419 return state_.load(std::memory_order_acquire); 420} 421 422void PGOProfiler::SetState(State state) 423{ 424 v_.AddLogWithDebugLog("[PGODumpStateChange] " + StateToString(GetState()) + " -> " + StateToString(state)); 425 state_.store(state, std::memory_order_release); 426} 427 428void PGOProfiler::NotifyGC(std::string tag) 429{ 430 v_.AddLogWithDebugLog(tag + " notify GC"); 431 condition_.SignalAll(); 432} 433 434void PGOProfiler::NotifyAll(std::string tag) 435{ 436 v_.AddLogWithDebugLog(tag + " notify all"); 437 condition_.SignalAll(); 438} 439 440void PGOProfiler::WaitingPGODump() 441{ 442 condition_.Wait(&mutex_); 443} 444 445void PGOProfiler::WaitPGODumpFinish() 446{ 447 if (!isEnable_) { 448 return; 449 } 450 LockHolder lock(mutex_); 451 while (GetState() == State::START) { 452 WaitingPGODump(); 453 } 454} 455 456void PGOProfiler::DumpByForce() 457{ 458 isForce_ = true; 459 LockHolder lock(mutex_); 460 if (GetState() == State::START) { 461 SetState(State::FORCE_SAVE); 462 WaitingPGODump(); 463 } else if (GetState() == State::STOP && !dumpWorkList_.IsEmpty()) { 464 SetState(State::FORCE_SAVE); 465 WaitingPGODump(); 466 DispatchPGODumpTask(); 467 } else if (GetState() == State::PAUSE) { 468 SetState(State::FORCE_SAVE_PAUSE); 469 WaitingPGODump(); 470 } 471} 472 473bool PGOProfiler::IsGCWaitingWithLock() 474{ 475 if (GetState() == State::PAUSE) { 476 LockHolder lock(mutex_); 477 if (GetState() == State::PAUSE) { 478 return true; 479 } 480 } 481 return false; 482} 483 484bool PGOProfiler::IsGCWaiting() 485{ 486 if (GetState() == State::PAUSE) { 487 return true; 488 } 489 return false; 490} 491 492void PGOProfiler::PGOPreDump(JSTaggedType func) 493{ 494 if (!isEnable_ || !vm_->GetJSOptions().IsEnableProfileDump()) { 495 return; 496 } 497 498 auto funcValue = JSTaggedValue(func); 499 if (!funcValue.IsJSFunction()) { 500 return; 501 } 502 auto methodValue = JSFunction::Cast(funcValue)->GetMethod(); 503 if (!methodValue.IsMethod()) { 504 return; 505 } 506 auto function = JSFunction::Cast(funcValue); 507 auto workNode = reinterpret_cast<WorkNode *>(function->GetWorkNodePointer()); 508 if (workNode == nullptr) { 509 workNode = nativeAreaAllocator_->New<WorkNode>(JSTaggedType(function)); 510 function->SetWorkNodePointer(reinterpret_cast<uintptr_t>(workNode)); 511 LockHolder lock(mutex_); 512 preDumpWorkList_.PushBack(workNode); 513 } else { 514 workNode->SetValue(JSTaggedType(function)); 515 auto workList = workNode->GetWorkList(); 516 LockHolder lock(mutex_); 517 if (workList == &dumpWorkList_) { 518 workList->Remove(workNode); 519 } 520 if (workList != &preDumpWorkList_) { 521 preDumpWorkList_.PushBack(workNode); 522 } 523 } 524} 525 526void PGOProfiler::UpdateExtraProfileTypeInfo(ApEntityId abcId, 527 const CString& recordName, 528 EntityId methodId, 529 WorkNode* current) 530{ 531 JSTaggedValue funcValue = JSTaggedValue(current->GetValue()); 532 if (!funcValue.IsJSFunction()) { 533 return; 534 } 535 auto func = JSFunction::Cast(funcValue); 536 if (!func->HasProfileTypeInfo(vm_->GetJSThread())) { 537 return; 538 } 539 ProfileTypeInfoCell *cell = ProfileTypeInfoCell::Cast(func->GetRawProfileTypeInfo()); 540 ProfileTypeInfo *info = ProfileTypeInfo::Cast((cell->GetValue()).GetTaggedObject()); 541 if ((info->GetExtraInfoMap()).IsHole() || (info->GetExtraInfoMap()).IsUndefined()) { 542 return; 543 } 544 NumberDictionary *dict = NumberDictionary::Cast(info->GetExtraInfoMap().GetTaggedObject()); 545 int size = dict->Size(); 546 for (int hashIndex = 0; hashIndex < size; hashIndex++) { 547 JSTaggedValue key(dict->GetKey(hashIndex)); 548 if (!key.IsUndefined() && !key.IsHole()) { 549 JSTaggedValue val(dict->GetValue(hashIndex)); 550 ExtraProfileTypeInfo *extraInfo = ExtraProfileTypeInfo::Cast(val.GetTaggedObject()); 551 if (!extraInfo->IsValid()) { 552 continue; 553 } 554 AddObjectInfo(abcId, 555 recordName, 556 methodId, 557 key.GetInt(), 558 extraInfo->GetReceiverHClass(), 559 extraInfo->GetReceiverHClass(), 560 extraInfo->GetHolderHClass()); 561 } 562 } 563} 564 565bool PGOProfiler::HasValidExtraProfileTypeInfo(JSFunction *func) 566{ 567 if (!func->HasProfileTypeInfo(vm_->GetJSThread())) { 568 return false; 569 } 570 ProfileTypeInfoCell *profileCell = ProfileTypeInfoCell::Cast(func->GetRawProfileTypeInfo()); 571 ProfileTypeInfo *profileInfo = ProfileTypeInfo::Cast((profileCell->GetValue()).GetTaggedObject()); 572 JSTaggedValue map = profileInfo->GetExtraInfoMap(); 573 if (map.IsHole() || map.IsUndefined()) { 574 return false; 575 } 576 NumberDictionary *numberDict = NumberDictionary::Cast(map.GetTaggedObject()); 577 return numberDict->GetEntrySize() > 0; 578} 579 580void PGOProfiler::ProcessExtraProfileTypeInfo(JSFunction *func, ApEntityId abcId, const CString &recordName, 581 JSTaggedValue methodValue, WorkNode *current) 582{ 583 if (!HasValidExtraProfileTypeInfo(func)) { 584 return; 585 } 586 Method* method = Method::Cast(methodValue.GetTaggedObject()); 587 EntityId methodId = method->GetMethodId(); 588 UpdateExtraProfileTypeInfo(abcId, recordName, methodId, current); 589} 590 591void PGOProfiler::HandlePGOPreDump() 592{ 593 LockHolder lock(recordInfoMutex_); 594 if (!isEnable_ || !vm_->GetJSOptions().IsEnableProfileDump()) { 595 return; 596 } 597 DISALLOW_GARBAGE_COLLECTION; 598 preDumpWorkList_.Iterate([this](WorkNode* current) { 599 JSTaggedValue funcValue = JSTaggedValue(current->GetValue()); 600 if (!funcValue.IsJSFunction()) { 601 return; 602 } 603 auto func = JSFunction::Cast(funcValue); 604 if (func->IsSendableOrConcurrentFunction()) { 605 return; 606 } 607 JSTaggedValue methodValue = func->GetMethod(); 608 if (!methodValue.IsMethod()) { 609 return; 610 } 611 CString recordName = func->GetRecordName(); 612 if (recordName.empty()) { 613 return; 614 } 615 auto abcId = GetMethodAbcId(func); 616 617 ProcessExtraProfileTypeInfo(func, abcId, recordName, methodValue, current); 618 ProfileType recordType = GetRecordProfileType(abcId, recordName); 619 recordInfos_->AddMethod(recordType, Method::Cast(methodValue), SampleMode::HOTNESS_MODE); 620 ProfileBytecode(abcId, recordName, funcValue); 621 if (PGOTrace::GetInstance()->IsEnable()) { 622 PGOTrace::GetInstance()->TryGetMethodData(methodValue, false); 623 } 624 }); 625} 626 627void PGOProfiler::HandlePGODumpByDumpThread(bool force) 628{ 629 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfiler::HandlePGODumpByDumpThread"); 630 LockHolder lock(recordInfoMutex_); 631 if (!isEnable_ || !vm_->GetJSOptions().IsEnableProfileDump()) { 632 return; 633 } 634 DISALLOW_GARBAGE_COLLECTION; 635 auto current = PopFromProfileQueue(); 636 while (current != nullptr) { 637 JSTaggedValue value = JSTaggedValue(current->GetValue()); 638 if (value.IsUndefined()) { 639 current = PopFromProfileQueue(); 640 continue; 641 } 642 if (!value.IsJSFunction()) { 643 current = PopFromProfileQueue(); 644 continue; 645 } 646 auto func = JSFunction::Cast(value); 647 if (func->IsSendableOrConcurrentFunction()) { 648 current = PopFromProfileQueue(); 649 continue; 650 } 651 JSTaggedValue methodValue = func->GetMethod(); 652 if (!methodValue.IsMethod()) { 653 current = PopFromProfileQueue(); 654 continue; 655 } 656 CString recordName = func->GetRecordName(); 657 if (recordName.empty()) { 658 current = PopFromProfileQueue(); 659 continue; 660 } 661 auto abcId = GetMethodAbcId(func); 662 663 ProcessExtraProfileTypeInfo(func, abcId, recordName, methodValue, current); 664 665 ProfileType recordType = GetRecordProfileType(abcId, recordName); 666 if (recordInfos_->AddMethod(recordType, Method::Cast(methodValue), SampleMode::HOTNESS_MODE)) { 667 methodCount_++; 668 } 669 ProfileBytecode(abcId, recordName, value); 670 current = PopFromProfileQueue(); 671 if (PGOTrace::GetInstance()->IsEnable()) { 672 PGOTrace::GetInstance()->TryGetMethodData(methodValue, true); 673 } 674 } 675 ASSERT(GetState() != State::STOP); 676 if (IsGCWaitingWithLock()) { 677 return; 678 } 679 MergeProfilerAndDispatchAsyncSaveTask(force); 680} 681 682void PGOProfiler::MergeProfilerAndDispatchAsyncSaveTask(bool force) 683{ 684 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfiler::MergeProfilerAndDispatchAsyncSaveTask"); 685 // Merged every 50 methods and merge interval greater than minimal interval 686 auto interval = std::chrono::system_clock::now() - saveTimestamp_; 687 auto minIntervalOption = vm_->GetJSOptions().GetPGOSaveMinInterval(); 688 auto mergeMinInterval = std::chrono::milliseconds(minIntervalOption * MS_PRE_SECOND); 689 if ((methodCount_ >= MERGED_EVERY_COUNT && interval > mergeMinInterval) || (force && methodCount_ > 0)) { 690 LOG_ECMA(DEBUG) << "Sample: post task to save profiler"; 691 { 692 ClockScope start; 693 PGOProfilerManager::GetInstance()->Merge(this); 694 if (PGOTrace::GetInstance()->IsEnable()) { 695 PGOTrace::GetInstance()->SetMergeTime(start.TotalSpentTime()); 696 } 697 } 698 if (!force) { 699 PGOProfilerManager::GetInstance()->AsyncSave(); 700 } 701 SetSaveTimestamp(std::chrono::system_clock::now()); 702 methodCount_ = 0; 703 } 704} 705 706PGOProfiler::WorkNode* PGOProfiler::PopFromProfileQueue() 707{ 708 LockHolder lock(mutex_); 709 WorkNode* node = nullptr; 710 while (node == nullptr) { 711 if (IsGCWaiting()) { 712 break; 713 } 714 if (dumpWorkList_.IsEmpty()) { 715 break; 716 } 717 node = dumpWorkList_.PopFront(); 718 } 719 return node; 720} 721 722void PGOProfiler::ProfileBytecode(ApEntityId abcId, const CString &recordName, JSTaggedValue funcValue) 723{ 724 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfiler::ProfileBytecode"); 725 ClockScope start; 726 JSFunction *function = JSFunction::Cast(funcValue); 727 if (function->IsSendableOrConcurrentFunction()) { 728 return; 729 } 730 Method *method = Method::Cast(function->GetMethod()); 731 JSTaggedValue profileTypeInfoVal = function->GetProfileTypeInfo(); 732 ASSERT(!profileTypeInfoVal.IsUndefined()); 733 auto profileTypeInfo = ProfileTypeInfo::Cast(profileTypeInfoVal.GetTaggedObject()); 734 auto methodId = method->GetMethodId(); 735 auto pcStart = method->GetBytecodeArray(); 736 auto codeSize = method->GetCodeSize(); 737 BytecodeInstruction bcIns(pcStart); 738 auto bcInsLast = bcIns.JumpTo(codeSize); 739 bool isForceDump = vm_->GetJSOptions().IsPgoForceDump(); 740 741 while (bcIns.GetAddress() != bcInsLast.GetAddress()) { 742 if (!isForceDump) { 743 if (IsGCWaitingWithLock()) { 744 break; 745 } 746 } 747 auto opcode = bcIns.GetOpcode(); 748 auto bcOffset = bcIns.GetAddress() - pcStart; 749 auto pc = bcIns.GetAddress(); 750 switch (opcode) { 751 case EcmaOpcode::LDTHISBYNAME_IMM8_ID16: 752 case EcmaOpcode::LDOBJBYNAME_IMM8_ID16: 753 case EcmaOpcode::LDPRIVATEPROPERTY_IMM8_IMM16_IMM16: { 754 uint8_t slotId = READ_INST_8_0(); 755 CHECK_SLOTID_BREAK(slotId); 756 DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD); 757 break; 758 } 759 case EcmaOpcode::LDTHISBYNAME_IMM16_ID16: 760 case EcmaOpcode::LDOBJBYNAME_IMM16_ID16: { 761 uint16_t slotId = READ_INST_16_0(); 762 DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD); 763 break; 764 } 765 case EcmaOpcode::LDOBJBYVALUE_IMM8_V8: 766 case EcmaOpcode::LDTHISBYVALUE_IMM8: { 767 uint8_t slotId = READ_INST_8_0(); 768 CHECK_SLOTID_BREAK(slotId); 769 DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD); 770 break; 771 } 772 case EcmaOpcode::LDOBJBYVALUE_IMM16_V8: 773 case EcmaOpcode::LDTHISBYVALUE_IMM16: { 774 uint16_t slotId = READ_INST_16_0(); 775 DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD); 776 break; 777 } 778 case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8: 779 case EcmaOpcode::STTHISBYNAME_IMM8_ID16: 780 case EcmaOpcode::DEFINEPROPERTYBYNAME_IMM8_ID16_V8: 781 case EcmaOpcode::STPRIVATEPROPERTY_IMM8_IMM16_IMM16_V8: { 782 uint8_t slotId = READ_INST_8_0(); 783 CHECK_SLOTID_BREAK(slotId); 784 DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE); 785 break; 786 } 787 case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8: 788 case EcmaOpcode::STTHISBYNAME_IMM16_ID16: { 789 uint16_t slotId = READ_INST_16_0(); 790 DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE); 791 break; 792 } 793 case EcmaOpcode::STOBJBYVALUE_IMM8_V8_V8: 794 case EcmaOpcode::STOWNBYINDEX_IMM8_V8_IMM16: 795 case EcmaOpcode::STTHISBYVALUE_IMM8_V8: { 796 uint8_t slotId = READ_INST_8_0(); 797 CHECK_SLOTID_BREAK(slotId); 798 DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE); 799 break; 800 } 801 case EcmaOpcode::STOBJBYVALUE_IMM16_V8_V8: 802 case EcmaOpcode::STOWNBYINDEX_IMM16_V8_IMM16: 803 case EcmaOpcode::STTHISBYVALUE_IMM16_V8: { 804 uint16_t slotId = READ_INST_16_0(); 805 DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE); 806 break; 807 } 808 // Op 809 case EcmaOpcode::ADD2_IMM8_V8: 810 case EcmaOpcode::SUB2_IMM8_V8: 811 case EcmaOpcode::MUL2_IMM8_V8: 812 case EcmaOpcode::DIV2_IMM8_V8: 813 case EcmaOpcode::MOD2_IMM8_V8: 814 case EcmaOpcode::SHL2_IMM8_V8: 815 case EcmaOpcode::SHR2_IMM8_V8: 816 case EcmaOpcode::AND2_IMM8_V8: 817 case EcmaOpcode::OR2_IMM8_V8: 818 case EcmaOpcode::XOR2_IMM8_V8: 819 case EcmaOpcode::ASHR2_IMM8_V8: 820 case EcmaOpcode::EXP_IMM8_V8: 821 case EcmaOpcode::NEG_IMM8: 822 case EcmaOpcode::NOT_IMM8: 823 case EcmaOpcode::INC_IMM8: 824 case EcmaOpcode::DEC_IMM8: 825 case EcmaOpcode::EQ_IMM8_V8: 826 case EcmaOpcode::NOTEQ_IMM8_V8: 827 case EcmaOpcode::LESS_IMM8_V8: 828 case EcmaOpcode::LESSEQ_IMM8_V8: 829 case EcmaOpcode::GREATER_IMM8_V8: 830 case EcmaOpcode::GREATEREQ_IMM8_V8: 831 case EcmaOpcode::STRICTNOTEQ_IMM8_V8: 832 case EcmaOpcode::STRICTEQ_IMM8_V8: 833 case EcmaOpcode::TONUMERIC_IMM8: { 834 uint8_t slotId = READ_INST_8_0(); 835 CHECK_SLOTID_BREAK(slotId); 836 DumpOpType(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 837 break; 838 } 839 case EcmaOpcode::CALLRUNTIME_ISTRUE_PREF_IMM8: 840 case EcmaOpcode::CALLRUNTIME_ISFALSE_PREF_IMM8: { 841 uint8_t slotId = READ_INST_8_1(); 842 CHECK_SLOTID_BREAK(slotId); 843 DumpOpType(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 844 break; 845 } 846 // Call 847 case EcmaOpcode::CALLARG0_IMM8: 848 case EcmaOpcode::CALLARG1_IMM8_V8: 849 case EcmaOpcode::CALLARGS2_IMM8_V8_V8: 850 case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8: 851 case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8: 852 case EcmaOpcode::CALLTHIS0_IMM8_V8: 853 case EcmaOpcode::CALLTHIS1_IMM8_V8_V8: 854 case EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8: 855 case EcmaOpcode::CALLTHIS3_IMM8_V8_V8_V8_V8: 856 case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8: { 857 uint8_t slotId = READ_INST_8_0(); 858 CHECK_SLOTID_BREAK(slotId); 859 DumpCall(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 860 break; 861 } 862 case EcmaOpcode::CALLRUNTIME_CALLINIT_PREF_IMM8_V8: { 863 uint8_t slotId = READ_INST_8_1(); 864 CHECK_SLOTID_BREAK(slotId); 865 DumpCall(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 866 break; 867 } 868 case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8: 869 case EcmaOpcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8: { 870 // no ic slot 871 break; 872 } 873 case EcmaOpcode::NEWOBJRANGE_IMM8_IMM8_V8: { 874 uint8_t slotId = READ_INST_8_0(); 875 CHECK_SLOTID_BREAK(slotId); 876 DumpNewObjRange(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 877 break; 878 } 879 case EcmaOpcode::NEWOBJRANGE_IMM16_IMM8_V8: { 880 uint16_t slotId = READ_INST_16_0(); 881 DumpNewObjRange(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 882 break; 883 } 884 case EcmaOpcode::WIDE_NEWOBJRANGE_PREF_IMM16_V8: { 885 break; 886 } 887 // Create object 888 case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8: { 889 uint8_t slotId = READ_INST_8_0(); 890 CHECK_SLOTID_BREAK(slotId); 891 DumpDefineClass(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 892 break; 893 } 894 case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: { 895 uint16_t slotId = READ_INST_16_0(); 896 DumpDefineClass(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 897 break; 898 } 899 case EcmaOpcode::DEFINEFUNC_IMM8_ID16_IMM8: { 900 uint8_t slotId = READ_INST_8_0(); 901 CHECK_SLOTID_BREAK(slotId); 902 DumpDefineClass(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 903 break; 904 } 905 case EcmaOpcode::DEFINEFUNC_IMM16_ID16_IMM8: { 906 uint16_t slotId = READ_INST_16_0(); 907 DumpDefineClass(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 908 break; 909 } 910 case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM8_ID16: 911 case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16: 912 case EcmaOpcode::CREATEEMPTYARRAY_IMM8: { 913 if (method->GetJSPandaFile() == nullptr) { 914 break; 915 } 916 auto header = method->GetJSPandaFile()->GetPandaFile()->GetHeader(); 917 auto traceId = 918 static_cast<int32_t>(reinterpret_cast<uintptr_t>(pc) - reinterpret_cast<uintptr_t>(header)); 919 uint8_t slotId = READ_INST_8_0(); 920 CHECK_SLOTID_BREAK(slotId); 921 DumpCreateObject(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, traceId); 922 break; 923 } 924 case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: 925 case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: 926 case EcmaOpcode::CREATEEMPTYARRAY_IMM16: { 927 if (method->GetJSPandaFile() == nullptr) { 928 break; 929 } 930 auto header = method->GetJSPandaFile()->GetPandaFile()->GetHeader(); 931 auto traceId = 932 static_cast<int32_t>(reinterpret_cast<uintptr_t>(pc) - reinterpret_cast<uintptr_t>(header)); 933 uint16_t slotId = READ_INST_16_0(); 934 DumpCreateObject(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, traceId); 935 break; 936 } 937 case EcmaOpcode::GETITERATOR_IMM8: { 938 uint8_t slotId = READ_INST_8_0(); 939 CHECK_SLOTID_BREAK(slotId); 940 DumpGetIterator(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 941 break; 942 } 943 case EcmaOpcode::GETITERATOR_IMM16: { 944 uint16_t slotId = READ_INST_16_0(); 945 DumpGetIterator(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 946 break; 947 } 948 // Others 949 case EcmaOpcode::INSTANCEOF_IMM8_V8: { 950 uint8_t slotId = READ_INST_8_0(); 951 CHECK_SLOTID_BREAK(slotId); 952 DumpInstanceof(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); 953 break; 954 } 955 case EcmaOpcode::DEFINEGETTERSETTERBYVALUE_V8_V8_V8_V8: 956 default: 957 break; 958 } 959 bcIns = bcIns.GetNext(); 960 } 961 if (PGOTrace::GetInstance()->IsEnable()) { 962 auto methodData = PGOTrace::GetInstance()->TryGetMethodData(function->GetMethod()); 963 methodData->SetProfileBytecodeTime(start.TotalSpentTime()); 964 } 965} 966 967void PGOProfiler::DumpICByName(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 968 uint32_t slotId, ProfileTypeInfo *profileTypeInfo, BCType type) 969{ 970 JSTaggedValue firstValue = profileTypeInfo->Get(slotId); 971 if (!firstValue.IsHeapObject()) { 972 if (firstValue.IsHole()) { 973 // Mega state 974 AddObjectInfoWithMega(abcId, recordName, methodId, bcOffset); 975 } 976 return; 977 } 978 if (firstValue.IsWeak()) { 979 TaggedObject *object = firstValue.GetWeakReferentUnChecked(); 980 if (object->GetClass()->IsHClass()) { 981 JSTaggedValue secondValue = profileTypeInfo->Get(slotId + 1); 982 JSHClass *hclass = JSHClass::Cast(object); 983 DumpICByNameWithHandler(abcId, recordName, methodId, bcOffset, hclass, secondValue, type); 984 } 985 return; 986 } 987 DumpICByNameWithPoly(abcId, recordName, methodId, bcOffset, firstValue, type); 988} 989 990void PGOProfiler::DumpICByValue(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 991 uint32_t slotId, ProfileTypeInfo *profileTypeInfo, BCType type) 992{ 993 JSTaggedValue firstValue = profileTypeInfo->Get(slotId); 994 if (!firstValue.IsHeapObject()) { 995 if (firstValue.IsHole()) { 996 // Mega state 997 AddObjectInfoWithMega(abcId, recordName, methodId, bcOffset); 998 } 999 return; 1000 } 1001 if (firstValue.IsWeak()) { 1002 TaggedObject *object = firstValue.GetWeakReferentUnChecked(); 1003 if (object->GetClass()->IsHClass()) { 1004 JSTaggedValue secondValue = profileTypeInfo->Get(slotId + 1); 1005 JSHClass *hclass = JSHClass::Cast(object); 1006 DumpICByValueWithHandler(abcId, recordName, methodId, bcOffset, hclass, secondValue, type); 1007 } 1008 return; 1009 } 1010 // Check key 1011 if ((firstValue.IsString() || firstValue.IsSymbol())) { 1012 return; 1013 } 1014 // Check without key 1015 DumpICByValueWithPoly(abcId, recordName, methodId, bcOffset, firstValue, type); 1016} 1017 1018void PGOProfiler::DumpICByNameWithPoly(ApEntityId abcId, 1019 const CString &recordName, EntityId methodId, int32_t bcOffset, JSTaggedValue cacheValue, BCType type) 1020{ 1021 if (cacheValue.IsWeak()) { 1022 return; 1023 } 1024 ASSERT(cacheValue.IsTaggedArray()); 1025 auto array = TaggedArray::Cast(cacheValue); 1026 uint32_t length = array->GetLength(); 1027 for (uint32_t i = 0; i < length; i += 2) { // 2 means one ic, two slot 1028 auto result = array->Get(i); 1029 auto handler = array->Get(i + 1); 1030 if (!result.IsHeapObject() || !result.IsWeak()) { 1031 continue; 1032 } 1033 TaggedObject *object = result.GetWeakReferentUnChecked(); 1034 if (!object->GetClass()->IsHClass()) { 1035 continue; 1036 } 1037 JSHClass *hclass = JSHClass::Cast(object); 1038 if (!DumpICByNameWithHandler(abcId, recordName, methodId, bcOffset, hclass, handler, type)) { 1039 AddObjectInfoWithMega(abcId, recordName, methodId, bcOffset); 1040 break; 1041 } 1042 } 1043} 1044 1045void PGOProfiler::DumpICByValueWithPoly(ApEntityId abcId, 1046 const CString &recordName, EntityId methodId, int32_t bcOffset, JSTaggedValue cacheValue, BCType type) 1047{ 1048 if (cacheValue.IsWeak()) { 1049 return; 1050 } 1051 ASSERT(cacheValue.IsTaggedArray()); 1052 auto array = TaggedArray::Cast(cacheValue); 1053 uint32_t length = array->GetLength(); 1054 for (uint32_t i = 0; i < length; i += 2) { // 2 means one ic, two slot 1055 auto result = array->Get(i); 1056 auto handler = array->Get(i + 1); 1057 if (!result.IsHeapObject() || !result.IsWeak()) { 1058 continue; 1059 } 1060 TaggedObject *object = result.GetWeakReferentUnChecked(); 1061 if (!object->GetClass()->IsHClass()) { 1062 continue; 1063 } 1064 JSHClass *hclass = JSHClass::Cast(object); 1065 DumpICByValueWithHandler(abcId, recordName, methodId, bcOffset, hclass, handler, type); 1066 } 1067} 1068 1069bool PGOProfiler::DumpICByNameWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, 1070 int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue, BCType type) 1071{ 1072 TryDumpProtoTransitionType(hclass); 1073 if (type == BCType::LOAD) { 1074 return DumpICLoadByNameWithHandler(abcId, recordName, methodId, bcOffset, hclass, secondValue); 1075 } 1076 1077 if (secondValue.IsInt()) { 1078 return AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, hclass); 1079 } else if (secondValue.IsTransitionHandler()) { 1080 auto transitionHandler = TransitionHandler::Cast(secondValue.GetTaggedObject()); 1081 auto transitionHClassVal = transitionHandler->GetTransitionHClass(); 1082 if (transitionHClassVal.IsJSHClass()) { 1083 auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject()); 1084 return AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, transitionHClass); 1085 } 1086 } else if (secondValue.IsTransWithProtoHandler()) { 1087 auto transWithProtoHandler = TransWithProtoHandler::Cast(secondValue.GetTaggedObject()); 1088 auto cellValue = transWithProtoHandler->GetProtoCell(); 1089 if (CheckProtoChangeMarker(cellValue)) { 1090 return false; 1091 } 1092 auto transitionHClassVal = transWithProtoHandler->GetTransitionHClass(); 1093 if (transitionHClassVal.IsJSHClass()) { 1094 auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject()); 1095 return AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, transitionHClass); 1096 } 1097 } else if (secondValue.IsPrototypeHandler()) { 1098 auto prototypeHandler = PrototypeHandler::Cast(secondValue.GetTaggedObject()); 1099 auto cellValue = prototypeHandler->GetProtoCell(); 1100 if (CheckProtoChangeMarker(cellValue)) { 1101 return false; 1102 } 1103 auto holder = prototypeHandler->GetHolder(); 1104 auto holderHClass = holder.GetTaggedObject()->GetClass(); 1105 auto accessorMethodId = prototypeHandler->GetAccessorMethodId(); 1106 return AddObjectInfo( 1107 abcId, recordName, methodId, bcOffset, hclass, holderHClass, holderHClass, accessorMethodId); 1108 } else if (secondValue.IsStoreTSHandler()) { 1109 StoreTSHandler *storeTSHandler = StoreTSHandler::Cast(secondValue.GetTaggedObject()); 1110 auto cellValue = storeTSHandler->GetProtoCell(); 1111 if (CheckProtoChangeMarker(cellValue)) { 1112 return false; 1113 } 1114 auto holder = storeTSHandler->GetHolder(); 1115 auto holderHClass = holder.GetTaggedObject()->GetClass(); 1116 return AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, holderHClass, holderHClass); 1117 } 1118 // StoreGlobal 1119 return false; 1120} 1121 1122bool PGOProfiler::DumpICLoadByNameWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, 1123 int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue) 1124{ 1125 bool ret = false; 1126 if (secondValue.IsInt()) { 1127 auto handlerInfo = static_cast<uint32_t>(secondValue.GetInt()); 1128 if (HandlerBase::IsNonExist(handlerInfo)) { 1129 return ret; 1130 } 1131 if (HandlerBase::IsField(handlerInfo) || HandlerBase::IsAccessor(handlerInfo)) { 1132 if (AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, hclass)) { 1133 return true; 1134 } 1135 } 1136 return AddBuiltinsInfoByNameInInstance(abcId, recordName, methodId, bcOffset, hclass); 1137 } else if (secondValue.IsPrototypeHandler()) { 1138 auto prototypeHandler = PrototypeHandler::Cast(secondValue.GetTaggedObject()); 1139 auto cellValue = prototypeHandler->GetProtoCell(); 1140 if (CheckProtoChangeMarker(cellValue)) { 1141 return ret; 1142 } 1143 auto holder = prototypeHandler->GetHolder(); 1144 auto holderHClass = holder.GetTaggedObject()->GetClass(); 1145 JSTaggedValue handlerInfoVal = prototypeHandler->GetHandlerInfo(); 1146 if (!handlerInfoVal.IsInt()) { 1147 return ret; 1148 } 1149 auto handlerInfo = static_cast<uint32_t>(handlerInfoVal.GetInt()); 1150 if (HandlerBase::IsNonExist(handlerInfo)) { 1151 return ret; 1152 } 1153 auto accessorMethodId = prototypeHandler->GetAccessorMethodId(); 1154 if (!AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, holderHClass, 1155 holderHClass, accessorMethodId)) { 1156 return AddBuiltinsInfoByNameInProt(abcId, recordName, methodId, bcOffset, hclass, holderHClass); 1157 } 1158 return true; 1159 } 1160 // LoadGlobal 1161 return false; 1162} 1163 1164void PGOProfiler::DumpICByValueWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, 1165 int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue, BCType type) 1166{ 1167 TryDumpProtoTransitionType(hclass); 1168 if (type == BCType::LOAD) { 1169 if (secondValue.IsInt()) { 1170 auto handlerInfo = static_cast<uint32_t>(secondValue.GetInt()); 1171 if (HandlerBase::IsNormalElement(handlerInfo) || HandlerBase::IsStringElement(handlerInfo)) { 1172 if (HandlerBase::NeedSkipInPGODump(handlerInfo)) { 1173 return; 1174 } 1175 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, hclass); 1176 return; 1177 } 1178 1179 if (HandlerBase::IsTypedArrayElement(handlerInfo)) { 1180 OnHeapMode onHeap = HandlerBase::IsOnHeap(handlerInfo) ? OnHeapMode::ON_HEAP : OnHeapMode::NOT_ON_HEAP; 1181 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, onHeap); 1182 return; 1183 } 1184 1185 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, hclass); 1186 } 1187 return; 1188 } 1189 if (secondValue.IsInt()) { 1190 auto handlerInfo = static_cast<uint32_t>(secondValue.GetInt()); 1191 if (HandlerBase::IsNormalElement(handlerInfo) || HandlerBase::IsStringElement(handlerInfo)) { 1192 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, 1193 OnHeapMode::NONE, HandlerBase::IsStoreOutOfBounds(handlerInfo)); 1194 return; 1195 } 1196 1197 if (HandlerBase::IsTypedArrayElement(handlerInfo)) { 1198 OnHeapMode onHeap = HandlerBase::IsOnHeap(handlerInfo) ? OnHeapMode::ON_HEAP : OnHeapMode::NOT_ON_HEAP; 1199 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, onHeap, 1200 HandlerBase::IsStoreOutOfBounds(handlerInfo)); 1201 return; 1202 } 1203 1204 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, hclass); 1205 } else if (secondValue.IsTransitionHandler()) { 1206 auto transitionHandler = TransitionHandler::Cast(secondValue.GetTaggedObject()); 1207 auto transitionHClassVal = transitionHandler->GetTransitionHClass(); 1208 1209 auto handlerInfoValue = transitionHandler->GetHandlerInfo(); 1210 ASSERT(handlerInfoValue.IsInt()); 1211 auto handlerInfo = static_cast<uint32_t>(handlerInfoValue.GetInt()); 1212 if (transitionHClassVal.IsJSHClass()) { 1213 auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject()); 1214 if (HandlerBase::IsElement(handlerInfo)) { 1215 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, transitionHClass, 1216 OnHeapMode::NONE, HandlerBase::IsStoreOutOfBounds(handlerInfo)); 1217 return; 1218 } 1219 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, transitionHClass); 1220 } 1221 } else if (secondValue.IsTransWithProtoHandler()) { 1222 auto transWithProtoHandler = TransWithProtoHandler::Cast(secondValue.GetTaggedObject()); 1223 auto transitionHClassVal = transWithProtoHandler->GetTransitionHClass(); 1224 1225 auto handlerInfoValue = transWithProtoHandler->GetHandlerInfo(); 1226 ASSERT(handlerInfoValue.IsInt()); 1227 auto handlerInfo = static_cast<uint32_t>(handlerInfoValue.GetInt()); 1228 if (transitionHClassVal.IsJSHClass()) { 1229 auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject()); 1230 if (HandlerBase::IsElement(handlerInfo)) { 1231 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, transitionHClass, 1232 OnHeapMode::NONE, HandlerBase::IsStoreOutOfBounds(handlerInfo)); 1233 return; 1234 } 1235 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, transitionHClass); 1236 } 1237 } else if (secondValue.IsPrototypeHandler()) { 1238 PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(secondValue.GetTaggedObject()); 1239 auto cellValue = prototypeHandler->GetProtoCell(); 1240 if (!cellValue.IsProtoChangeMarker()) { 1241 return; 1242 } 1243 ASSERT(cellValue.IsProtoChangeMarker()); 1244 ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject()); 1245 if (cell->GetHasChanged()) { 1246 return; 1247 } 1248 JSTaggedValue handlerInfoValue = prototypeHandler->GetHandlerInfo(); 1249 ASSERT(handlerInfoValue.IsInt()); 1250 auto handlerInfo = static_cast<uint32_t>(handlerInfoValue.GetInt()); 1251 if (HandlerBase::IsElement(handlerInfo)) { 1252 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, 1253 OnHeapMode::NONE, HandlerBase::IsStoreOutOfBounds(handlerInfo)); 1254 return; 1255 } 1256 auto holder = prototypeHandler->GetHolder(); 1257 auto holderHClass = holder.GetTaggedObject()->GetClass(); 1258 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, holderHClass, holderHClass); 1259 } 1260} 1261 1262void PGOProfiler::TryDumpProtoTransitionType(JSHClass *hclass) 1263{ 1264 JSHClass *ihc1 = JSHClass::FindRootHClass(hclass); 1265 auto transitionType = GetProfileType(ihc1, true); 1266 if (!transitionType.IsRootType() || !transitionType.IsTransitionClassType()) { 1267 return; 1268 } 1269 JSTaggedValue phc1Root = JSHClass::FindProtoRootHClass(ihc1); 1270 auto transitionProtoType = GetProfileType(JSHClass::Cast(phc1Root.GetTaggedObject()), true); 1271 if (!transitionProtoType.IsRootType()) { 1272 LOG_ECMA(DEBUG) << "Set as the prototype of a function again after transition happened for this prototype!"; 1273 return; 1274 } 1275 1276 auto thread = vm_->GetJSThread(); 1277 auto *transitionTable = thread->GetCurrentEcmaContext()->GetFunctionProtoTransitionTable(); 1278 JSTaggedType ihc0 = transitionTable->GetFakeParent(JSTaggedType(ihc1)); 1279 JSTaggedType baseIhc = transitionTable->GetFakeParent(phc1Root.GetRawData()); 1280 if ((ihc0 == 0) || (baseIhc == 0)) { 1281 return; 1282 } 1283 1284 auto ihc0Obj = JSHClass::Cast(JSTaggedValue(ihc0).GetTaggedObject()); 1285 auto baseIhcObj = JSHClass::Cast(JSTaggedValue(baseIhc).GetTaggedObject()); 1286 UpdateLayout(ihc0Obj); 1287 UpdateLayout(ihc1); 1288 UpdateLayout(baseIhcObj); 1289 1290 auto ihc0RootType = GetProfileType(ihc0Obj); 1291 ASSERT(ihc0RootType.IsRootType()); 1292 auto baseRootHClass = JSHClass::FindRootHClass(baseIhcObj); 1293 auto baseRootType = GetProfileType(baseRootHClass, true); 1294 if (!baseRootType.IsRootType()) { 1295 LOG_ECMA(DEBUG) << "Unsupported prototypes which cannot be recorded!"; 1296 return; 1297 } 1298 auto baseType = GetProfileType(baseIhcObj); 1299 ASSERT(!baseType.IsNone()); 1300 PGOProtoTransitionType protoTransitionType(ihc0RootType); 1301 protoTransitionType.SetBaseType(baseRootType, baseType); 1302 protoTransitionType.SetTransitionType(transitionType); 1303 protoTransitionType.SetTransitionProtoPt(transitionProtoType); 1304 1305 recordInfos_->GetProtoTransitionPool()->Add(protoTransitionType); 1306} 1307 1308void PGOProfiler::DumpOpType(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 1309 uint32_t slotId, ProfileTypeInfo *profileTypeInfo) 1310{ 1311 JSTaggedValue slotValue = profileTypeInfo->Get(slotId); 1312 if (slotValue.IsInt()) { 1313 auto type = slotValue.GetInt(); 1314 ProfileType recordType = GetRecordProfileType(abcId, recordName); 1315 recordInfos_->AddType(recordType, methodId, bcOffset, PGOSampleType(type)); 1316 } 1317} 1318 1319bool PGOProfiler::FunctionKindVerify(const JSFunction *ctorFunction) 1320{ 1321 FunctionKind kind = Method::Cast(ctorFunction->GetMethod())->GetFunctionKind(); 1322 return kind == FunctionKind::BASE_CONSTRUCTOR || 1323 kind == FunctionKind::CLASS_CONSTRUCTOR || 1324 kind == FunctionKind::DERIVED_CONSTRUCTOR; 1325} 1326 1327void PGOProfiler::DumpDefineClass(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 1328 uint32_t slotId, ProfileTypeInfo *profileTypeInfo) 1329{ 1330 JSTaggedValue slotValue = profileTypeInfo->Get(slotId); 1331 if (!slotValue.IsProfileTypeInfoCell0()) { 1332 return; 1333 } 1334 JSTaggedValue handle = ProfileTypeInfoCell::Cast(slotValue)->GetHandle(); 1335 if (!handle.IsHeapObject() || !handle.IsWeak()) { 1336 return; 1337 } 1338 auto object = handle.GetWeakReferentUnChecked(); 1339 if (object->GetClass()->IsJSFunction()) { 1340 JSFunction *ctorFunction = JSFunction::Cast(object); 1341 auto ctorMethod = ctorFunction->GetMethod(); 1342 if (!ctorMethod.IsMethod() || !FunctionKindVerify(ctorFunction)) { 1343 return; 1344 } 1345 ApEntityId ctorAbcId = GetMethodAbcId(ctorFunction); 1346 auto ctorJSMethod = Method::Cast(ctorMethod); 1347 auto ctorMethodId = ctorJSMethod->GetMethodId().GetOffset(); 1348 1349 auto localType = ProfileType(ctorAbcId, ctorMethodId, ProfileType::Kind::ClassId, true); 1350 if (IsSkippableObjectTypeSafe(localType)) { 1351 return; 1352 } 1353 PGODefineOpType objDefType(localType); 1354 auto protoOrHClass = ctorFunction->GetProtoOrHClass(); 1355 if (protoOrHClass.IsJSHClass()) { 1356 auto ihc = JSHClass::Cast(protoOrHClass.GetTaggedObject()); 1357 SetRootProfileType(ihc, ctorAbcId, ctorMethodId, ProfileType::Kind::ClassId); 1358 recordInfos_->AddRootLayout(JSTaggedType(ihc), localType); 1359 protoOrHClass = ihc->GetProto(); 1360 } 1361 1362 auto ctorRootHClass = JSHClass::FindRootHClass(ctorFunction->GetJSHClass()); 1363 auto ctorType = GetProfileType(ctorRootHClass); 1364 if (!ctorType.IsRootType()) { 1365 LOG_ECMA(DEBUG) << "The profileType of constructor root hclass was not found."; 1366 } else { 1367 objDefType.SetCtorPt(ctorType); 1368 recordInfos_->AddRootLayout(JSTaggedType(ctorRootHClass), ctorType); 1369 } 1370 1371 if (protoOrHClass.IsJSObject()) { 1372 auto prototypeHClass = JSObject::Cast(protoOrHClass)->GetClass(); 1373 auto prototypeRootHClass = JSHClass::FindRootHClass(prototypeHClass); 1374 ProfileType prototypeType = GetProfileType(prototypeRootHClass); 1375 if (!prototypeType.IsRootType()) { 1376 LOG_ECMA(DEBUG) << "The profileType of prototype root hclass was not found."; 1377 } else { 1378 objDefType.SetProtoTypePt(prototypeType); 1379 recordInfos_->AddRootLayout(JSTaggedType(prototypeRootHClass), prototypeType); 1380 } 1381 } 1382 1383 ProfileType recordType = GetRecordProfileType(abcId, recordName); 1384 recordInfos_->AddDefine(recordType, methodId, bcOffset, objDefType); 1385 } 1386} 1387 1388void PGOProfiler::DumpCreateObject(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 1389 uint32_t slotId, ProfileTypeInfo *profileTypeInfo, int32_t traceId) 1390{ 1391 JSTaggedValue slotValue = profileTypeInfo->Get(slotId); 1392 if (!slotValue.IsHeapObject()) { 1393 return; 1394 } 1395 ProfileType recordType = GetRecordProfileType(abcId, recordName); 1396 if (slotValue.IsWeak()) { 1397 auto object = slotValue.GetWeakReferentUnChecked(); 1398 if (object->GetClass()->IsHClass()) { 1399 auto newHClass = JSHClass::Cast(object); 1400 auto rootHClass = JSHClass::FindRootHClass(newHClass); 1401 ProfileType profileType = GetProfileType(rootHClass); 1402 if (!profileType.IsRootType()) { 1403 return; 1404 } 1405 ASSERT(profileType.GetKind() == ProfileType::Kind::ObjectLiteralId); 1406 PGOSampleType currentType(profileType); 1407 PGODefineOpType objDefType(profileType); 1408 recordInfos_->AddDefine(recordType, methodId, bcOffset, objDefType); 1409 recordInfos_->AddRootLayout(JSTaggedType(rootHClass), profileType); 1410 } 1411 } else if (slotValue.IsTrackInfoObject()) { 1412 auto currentType = PGOSampleType::CreateProfileType(abcId, traceId, ProfileType::Kind::ArrayLiteralId, true); 1413 auto profileType = currentType.GetProfileType(); 1414 PGODefineOpType objDefType(profileType); 1415 TrackInfo *trackInfo = TrackInfo::Cast(slotValue.GetTaggedObject()); 1416 auto elementsKind = trackInfo->GetElementsKind(); 1417 objDefType.SetElementsKind(elementsKind); 1418 objDefType.SetElementsLength(trackInfo->GetArrayLength()); 1419 objDefType.SetSpaceFlag(trackInfo->GetSpaceFlag()); 1420 recordInfos_->AddDefine(recordType, methodId, bcOffset, objDefType); 1421 auto cachedHClass = trackInfo->GetCachedHClass(); 1422 if (cachedHClass.IsJSHClass()) { 1423 auto hclass = JSHClass::Cast(cachedHClass.GetTaggedObject()); 1424 recordInfos_->AddRootLayout(JSTaggedType(hclass), profileType); 1425 } 1426 } 1427} 1428 1429void PGOProfiler::DumpCall(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 1430 uint32_t slotId, ProfileTypeInfo *profileTypeInfo) 1431{ 1432 JSTaggedValue slotValue = profileTypeInfo->Get(slotId); 1433 ProfileType::Kind kind; 1434 int calleeMethodId = 0; 1435 ApEntityId calleeAbcId = 0; 1436 if (slotValue.IsInt()) { 1437 calleeMethodId = slotValue.GetInt(); 1438 calleeAbcId = abcId; 1439 ASSERT(calleeMethodId <= 0); 1440 if (calleeMethodId == 0) { 1441 kind = ProfileType::Kind::MethodId; 1442 } else { 1443 kind = ProfileType::Kind::BuiltinFunctionId; 1444 } 1445 } else if (slotValue.IsJSFunction()) { 1446 JSFunction *callee = JSFunction::Cast(slotValue); 1447 Method *calleeMethod = Method::Cast(callee->GetMethod()); 1448 calleeMethodId = static_cast<int>(calleeMethod->GetMethodId().GetOffset()); 1449 calleeAbcId = GetMethodAbcId(callee->GetMethod()); 1450 kind = ProfileType::Kind::MethodId; 1451 } else { 1452 return; 1453 } 1454 PGOSampleType type = PGOSampleType::CreateProfileType(calleeAbcId, std::abs(calleeMethodId), kind); 1455 ProfileType recordType = GetRecordProfileType(abcId, recordName); 1456 recordInfos_->AddCallTargetType(recordType, methodId, bcOffset, type); 1457} 1458 1459void PGOProfiler::DumpGetIterator(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 1460 uint32_t slotId, ProfileTypeInfo *profileTypeInfo) 1461{ 1462 if (vm_->GetJSThread()->GetEnableLazyBuiltins()) { 1463 return; 1464 } 1465 JSTaggedValue value = profileTypeInfo->Get(slotId); 1466 if (!value.IsInt()) { 1467 return; 1468 } 1469 int iterKind = value.GetInt(); 1470 ASSERT(iterKind <= 0); 1471 ProfileType::Kind pgoKind = ProfileType::Kind::BuiltinFunctionId; 1472 PGOSampleType type = PGOSampleType::CreateProfileType(abcId, std::abs(iterKind), pgoKind); 1473 ProfileType recordType = GetRecordProfileType(abcId, recordName); 1474 recordInfos_->AddCallTargetType(recordType, methodId, bcOffset, type); 1475} 1476 1477void PGOProfiler::DumpNewObjRange(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 1478 uint32_t slotId, ProfileTypeInfo *profileTypeInfo) 1479{ 1480 JSTaggedValue slotValue = profileTypeInfo->Get(slotId); 1481 int ctorMethodId = 0; 1482 if (slotValue.IsInt()) { 1483 ctorMethodId = slotValue.GetInt(); 1484 } else if (slotValue.IsJSFunction()) { 1485 JSFunction *callee = JSFunction::Cast(slotValue); 1486 Method *calleeMethod = Method::Cast(callee->GetMethod()); 1487 ctorMethodId = static_cast<int>(calleeMethod->GetMethodId().GetOffset()); 1488 } else { 1489 return; 1490 } 1491 PGOSampleType type; 1492 if (ctorMethodId > 0) { 1493 type = PGOSampleType::CreateProfileType(abcId, ctorMethodId, ProfileType::Kind::ClassId, true); 1494 } else { 1495 auto kind = ProfileType::Kind::BuiltinFunctionId; 1496 type = PGOSampleType::CreateProfileType(abcId, std::abs(ctorMethodId), kind); 1497 } 1498 ProfileType recordType = GetRecordProfileType(abcId, recordName); 1499 recordInfos_->AddCallTargetType(recordType, methodId, bcOffset, type); 1500} 1501 1502void PGOProfiler::DumpInstanceof(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 1503 uint32_t slotId, ProfileTypeInfo *profileTypeInfo) 1504{ 1505 JSTaggedValue firstValue = profileTypeInfo->Get(slotId); 1506 if (!firstValue.IsHeapObject()) { 1507 if (firstValue.IsHole()) { 1508 // Mega state 1509 AddObjectInfoWithMega(abcId, recordName, methodId, bcOffset); 1510 } 1511 return; 1512 } 1513 if (firstValue.IsWeak()) { 1514 TaggedObject *object = firstValue.GetWeakReferentUnChecked(); 1515 if (object->GetClass()->IsHClass()) { 1516 JSHClass *hclass = JSHClass::Cast(object); 1517 // Since pgo does not support symbol, we choose to return if hclass having @@hasInstance 1518 JSHandle<GlobalEnv> env = vm_->GetGlobalEnv(); 1519 JSTaggedValue key = env->GetHasInstanceSymbol().GetTaggedValue(); 1520 JSHClass *functionPrototypeHC = JSObject::Cast(env->GetFunctionPrototype().GetTaggedValue())->GetClass(); 1521 JSTaggedValue foundHClass = TryFindKeyInPrototypeChain(object, hclass, key); 1522 if (!foundHClass.IsUndefined() && JSHClass::Cast(foundHClass.GetTaggedObject()) != functionPrototypeHC) { 1523 return; 1524 } 1525 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, hclass); 1526 } 1527 return; 1528 } 1529 // Poly Not Consider now 1530 return; 1531} 1532 1533void PGOProfiler::UpdateLayout(JSHClass *hclass) 1534{ 1535 auto parentHClass = hclass->GetParent(); 1536 if (!GetProfileType(hclass).IsRootType() && parentHClass.IsJSHClass()) { 1537 UpdateTransitionLayout(JSHClass::Cast(parentHClass.GetTaggedObject()), hclass); 1538 } else { 1539 auto rootHClass = JSHClass::FindRootHClass(hclass); 1540 ProfileType rootType = GetProfileType(rootHClass, true); 1541 if (!rootType.IsRootType()) { 1542 return; 1543 } 1544 1545 auto prototypeHClass = JSHClass::FindProtoRootHClass(rootHClass); 1546 if (prototypeHClass.IsJSHClass()) { 1547 auto prototypeObject = JSHClass::Cast(prototypeHClass.GetTaggedObject()); 1548 ProfileType prototypeType = GetProfileType(prototypeObject, true); 1549 if (prototypeType.IsRootType()) { 1550 recordInfos_->AddRootPtType(rootType, prototypeType); 1551 UpdateLayout(JSHClass::Cast(prototypeHClass.GetTaggedObject())); 1552 } 1553 } 1554 1555 auto curType = GetOrInsertProfileType(hclass, rootType); 1556 recordInfos_->UpdateLayout(rootType, JSTaggedType(hclass), curType); 1557 } 1558} 1559 1560void PGOProfiler::UpdateTransitionLayout(JSHClass* parent, JSHClass* child) 1561{ 1562 auto rootHClass = JSHClass::FindRootHClass(parent); 1563 auto rootType = GetProfileType(rootHClass, true); 1564 if (!rootType.IsRootType()) { 1565 return; 1566 } 1567 // If the child hclass is set as a prototype, it will become the root hclass. Need to give up. 1568 if (GetProfileType(child).IsRootType()) { 1569 return; 1570 } 1571 CStack<JSHClass *> hclassVec; 1572 hclassVec.emplace(child); 1573 hclassVec.emplace(parent); 1574 1575 while (!GetProfileType(parent).IsRootType()) { 1576 auto parentHCValue = parent->GetParent(); 1577 if (!parentHCValue.IsJSHClass()) { 1578 break; 1579 } 1580 parent = JSHClass::Cast(parentHCValue.GetTaggedObject()); 1581 hclassVec.emplace(parent); 1582 } 1583 1584 auto prototypeRootHClassVal = JSHClass::FindProtoRootHClass(rootHClass); 1585 if (prototypeRootHClassVal.IsJSHClass()) { 1586 auto prototypeRootHClass = JSHClass::Cast(prototypeRootHClassVal.GetTaggedObject()); 1587 auto prototypeType = GetProfileType(prototypeRootHClass); 1588 if (prototypeType.IsRootType()) { 1589 recordInfos_->AddRootPtType(rootType, prototypeType); 1590 UpdateLayout(prototypeRootHClass); 1591 } 1592 } 1593 1594 parent = hclassVec.top(); 1595 hclassVec.pop(); 1596 auto parentType = GetProfileType(parent); 1597 while (!hclassVec.empty()) { 1598 child = hclassVec.top(); 1599 hclassVec.pop(); 1600 auto childType = GetOrInsertProfileType(child, rootType); 1601 recordInfos_->UpdateTransitionLayout( 1602 rootType, JSTaggedType(parent), parentType, JSTaggedType(child), childType); 1603 parentType = childType; 1604 parent = child; 1605 } 1606} 1607 1608bool PGOProfiler::AddTransitionObjectInfo(ProfileType recordType, 1609 EntityId methodId, 1610 int32_t bcOffset, 1611 JSHClass* receiver, 1612 JSHClass* hold, 1613 JSHClass* holdTra, 1614 PGOSampleType accessorMethod) 1615{ 1616 auto receiverRootType = FindRootProfileType(receiver); 1617 if (!receiverRootType.IsRootType()) { 1618 return false; 1619 } 1620 1621 auto holdRootType = FindRootProfileType(hold); 1622 if (!holdRootType.IsRootType()) { 1623 return true; 1624 } 1625 1626 auto receiverType = GetOrInsertProfileType(receiver, receiverRootType); 1627 auto holdType = GetOrInsertProfileType(hold, holdRootType); 1628 auto holdTraType = GetOrInsertProfileType(holdTra, holdRootType); 1629 1630 if (receiver != hold) { 1631 UpdateLayout(receiver); 1632 } 1633 1634 if (holdType == holdTraType) { 1635 UpdateLayout(hold); 1636 } else { 1637 UpdateTransitionLayout(hold, holdTra); 1638 } 1639 1640 PGOObjectInfo info(receiverRootType, receiverType, holdRootType, holdType, holdRootType, holdTraType, 1641 accessorMethod); 1642 UpdatePrototypeChainInfo(receiver, hold, info); 1643 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info); 1644 return true; 1645} 1646 1647bool PGOProfiler::AddObjectInfo(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 1648 JSHClass *receiver, JSHClass *hold, JSHClass *holdTra, uint32_t accessorMethodId) 1649{ 1650 PGOSampleType accessor = PGOSampleType::CreateProfileType(abcId, accessorMethodId, ProfileType::Kind::MethodId); 1651 ProfileType recordType = GetRecordProfileType(abcId, recordName); 1652 return AddTransitionObjectInfo(recordType, methodId, bcOffset, receiver, hold, holdTra, accessor); 1653} 1654 1655void PGOProfiler::UpdatePrototypeChainInfo(JSHClass *receiver, JSHClass *holder, PGOObjectInfo &info) 1656{ 1657 if (receiver == holder) { 1658 return; 1659 } 1660 1661 std::vector<std::pair<ProfileType, ProfileType>> protoChain; 1662 JSTaggedValue proto = JSHClass::FindProtoHClass(receiver); 1663 while (proto.IsJSHClass()) { 1664 auto protoHClass = JSHClass::Cast(proto.GetTaggedObject()); 1665 if (protoHClass == holder) { 1666 break; 1667 } 1668 auto protoRootType = FindRootProfileType(protoHClass); 1669 if (!protoRootType.IsRootType()) { 1670 break; 1671 } 1672 auto protoType = GetOrInsertProfileType(protoHClass, protoRootType); 1673 protoChain.emplace_back(protoRootType, protoType); 1674 proto = JSHClass::FindProtoHClass(protoHClass); 1675 } 1676 if (!protoChain.empty()) { 1677 info.AddPrototypePt(protoChain); 1678 } 1679} 1680 1681void PGOProfiler::AddObjectInfoWithMega( 1682 ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset) 1683{ 1684 auto megaType = ProfileType::CreateMegaType(); 1685 PGOObjectInfo info(megaType, megaType, megaType, megaType, megaType, megaType, PGOSampleType()); 1686 ProfileType recordType = GetRecordProfileType(abcId, recordName); 1687 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info); 1688} 1689 1690bool PGOProfiler::AddBuiltinsInfoByNameInInstance(ApEntityId abcId, const CString &recordName, EntityId methodId, 1691 int32_t bcOffset, JSHClass *receiver) 1692{ 1693 auto thread = vm_->GetJSThread(); 1694 auto type = receiver->GetObjectType(); 1695 const auto &ctorEntries = thread->GetCtorHclassEntries(); 1696 auto entry = ctorEntries.find(receiver); 1697 if (entry != ctorEntries.end()) { 1698 AddBuiltinsGlobalInfo(abcId, recordName, methodId, bcOffset, entry->second); 1699 return true; 1700 } 1701 1702 auto builtinsId = ToBuiltinsTypeId(type); 1703 if (!builtinsId.has_value()) { 1704 return false; 1705 } 1706 JSHClass *exceptRecvHClass = nullptr; 1707 if (builtinsId == BuiltinTypeId::ARRAY) { 1708 bool receiverIsPrototype = receiver->IsPrototype(); 1709 exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype); 1710 } else if (builtinsId == BuiltinTypeId::STRING) { 1711 exceptRecvHClass = receiver; 1712 } else { 1713 exceptRecvHClass = thread->GetBuiltinInstanceHClass(builtinsId.value()); 1714 } 1715 1716 if (exceptRecvHClass != receiver) { 1717 // When JSType cannot uniquely identify builtins object, it is necessary to 1718 // query the receiver on the global constants. 1719 if (builtinsId == BuiltinTypeId::OBJECT) { 1720 exceptRecvHClass = JSHClass::Cast(thread->GlobalConstants()->GetIteratorResultClass().GetTaggedObject()); 1721 if (exceptRecvHClass == receiver) { 1722 GlobalIndex globalsId; 1723 globalsId.UpdateGlobalConstId(static_cast<size_t>(ConstantIndex::ITERATOR_RESULT_CLASS)); 1724 AddBuiltinsGlobalInfo(abcId, recordName, methodId, bcOffset, globalsId); 1725 return true; 1726 } 1727 } 1728 return false; 1729 } 1730 1731 return AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, receiver, receiver); 1732} 1733 1734bool PGOProfiler::AddBuiltinsInfoByNameInProt(ApEntityId abcId, const CString &recordName, EntityId methodId, 1735 int32_t bcOffset, JSHClass *receiver, JSHClass *hold) 1736{ 1737 auto type = receiver->GetObjectType(); 1738 auto builtinsId = ToBuiltinsTypeId(type); 1739 if (!builtinsId.has_value()) { 1740 return false; 1741 } 1742 auto thread = vm_->GetJSThread(); 1743 JSHClass *exceptRecvHClass = nullptr; 1744 if (builtinsId == BuiltinTypeId::ARRAY) { 1745 bool receiverIsPrototype = receiver->IsPrototype(); 1746 exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype); 1747 } else if (builtinsId == BuiltinTypeId::STRING) { 1748 exceptRecvHClass = receiver; 1749 } else { 1750 exceptRecvHClass = thread->GetBuiltinInstanceHClass(builtinsId.value()); 1751 } 1752 1753 auto exceptHoldHClass = thread->GetBuiltinPrototypeHClass(builtinsId.value()); 1754 auto exceptPrototypeOfPrototypeHClass = 1755 thread->GetBuiltinPrototypeOfPrototypeHClass(builtinsId.value()); 1756 // iterator needs to find two layers of prototype 1757 if (builtinsId == BuiltinTypeId::ARRAY_ITERATOR) { 1758 if ((exceptRecvHClass != receiver) || 1759 (exceptHoldHClass != hold && exceptPrototypeOfPrototypeHClass != hold)) { 1760 return false; 1761 } 1762 } else if (IsTypedArrayType(builtinsId.value())) { 1763 auto exceptRecvHClassOnHeap = thread->GetBuiltinExtraHClass(builtinsId.value()); 1764 ASSERT_PRINT(exceptRecvHClassOnHeap == nullptr || exceptRecvHClassOnHeap->IsOnHeapFromBitField(), 1765 "must be on heap"); 1766 if (IsJSHClassNotEqual(receiver, hold, exceptRecvHClass, exceptRecvHClassOnHeap, 1767 exceptHoldHClass, exceptPrototypeOfPrototypeHClass)) { 1768 return false; 1769 } 1770 } else if (exceptRecvHClass != receiver || exceptHoldHClass != hold) { 1771 return false; 1772 } 1773 1774 return AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, receiver, receiver); 1775} 1776 1777bool PGOProfiler::IsJSHClassNotEqual(JSHClass *receiver, JSHClass *hold, JSHClass *exceptRecvHClass, 1778 JSHClass *exceptRecvHClassOnHeap, JSHClass *exceptHoldHClass, 1779 JSHClass *exceptPrototypeOfPrototypeHClass) 1780{ 1781 //exceptRecvHClass = IHC, exceptRecvHClassOnHeap = IHC OnHeap 1782 //exceptHoldHClass = PHC, exceptPrototypeOfPrototypeHClass = HClass of X.prototype.prototype 1783 return ((exceptRecvHClass != receiver && exceptRecvHClassOnHeap != receiver) || 1784 (exceptHoldHClass != hold && exceptPrototypeOfPrototypeHClass != hold)); 1785} 1786 1787bool PGOProfiler::CheckProtoChangeMarker(JSTaggedValue cellValue) const 1788{ 1789 if (!cellValue.IsProtoChangeMarker()) { 1790 return true; 1791 } 1792 ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject()); 1793 return cell->GetHasChanged(); 1794} 1795 1796void PGOProfiler::AddBuiltinsGlobalInfo(ApEntityId abcId, const CString &recordName, EntityId methodId, 1797 int32_t bcOffset, GlobalIndex globalsId) 1798{ 1799 ProfileType recordType = GetRecordProfileType(abcId, recordName); 1800 PGOObjectInfo info(ProfileType::CreateGlobals(abcId, globalsId)); 1801 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info); 1802} 1803 1804bool PGOProfiler::AddBuiltinsInfo( 1805 ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSHClass *receiver, 1806 JSHClass *transitionHClass, OnHeapMode onHeap, bool everOutOfBounds) 1807{ 1808 ProfileType recordType = GetRecordProfileType(abcId, recordName); 1809 if (receiver->IsJSArray()) { 1810 auto type = receiver->GetObjectType(); 1811 auto elementsKind = receiver->GetElementsKind(); 1812 auto transitionElementsKind = transitionHClass->GetElementsKind(); 1813 auto profileType = ProfileType::CreateBuiltinsArray(abcId, type, elementsKind, transitionElementsKind, 1814 everOutOfBounds); 1815 PGOObjectInfo info(profileType); 1816 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info); 1817 } else if (receiver->IsTypedArray()) { 1818 JSType jsType = receiver->GetObjectType(); 1819 auto profileType = ProfileType::CreateBuiltinsTypedArray(abcId, jsType, onHeap, everOutOfBounds); 1820 PGOObjectInfo info(profileType); 1821 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info); 1822 } else { 1823 auto type = receiver->GetObjectType(); 1824 PGOObjectInfo info(ProfileType::CreateBuiltins(abcId, type)); 1825 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info); 1826 } 1827 return true; 1828} 1829 1830bool PGOProfiler::IsRecoredTransRootType(ProfileType type) 1831{ 1832 if (!type.IsRootType() || !type.IsTransitionType()) { 1833 return false; 1834 } 1835 if (std::find(recordedTransRootType_.begin(), recordedTransRootType_.end(), type) != recordedTransRootType_.end()) { 1836 LOG_ECMA(DEBUG) << "forbide to add more than 1 hclass for a root type!"; 1837 return true; 1838 } 1839 recordedTransRootType_.emplace_back(type); 1840 return false; 1841} 1842 1843void PGOProfiler::SetRootProfileType(JSHClass *root, ApEntityId abcId, uint32_t type, ProfileType::Kind kind) 1844{ 1845 ProfileType traceType(root->GetProfileType()); 1846 if (traceType.IsNone()) { 1847 traceType = ProfileType(abcId, type, kind, true); 1848 if (IsRecoredTransRootType(traceType)) { 1849 return; 1850 } 1851 root->SetProfileType(traceType.GetRaw()); 1852 } 1853} 1854 1855ProfileType PGOProfiler::FindRootProfileType(JSHClass *hclass) 1856{ 1857 auto rootHClass = JSHClass::FindRootHClass(hclass); 1858 return GetProfileType(rootHClass, true); 1859} 1860 1861ProfileType PGOProfiler::GetOrInsertProfileType(JSHClass *child, ProfileType rootType) 1862{ 1863 ProfileType childType = GetProfileType(child); 1864 if (childType.IsNone()) { 1865 ASSERT(rootType.IsRootType()); 1866 childType = PGOTypeGenerator::GenerateProfileType(JSTaggedType(child), rootType); 1867 child->SetProfileType(childType.GetRaw()); 1868 } 1869 return childType; 1870} 1871 1872ProfileType PGOProfiler::GetProfileType(JSHClass *hclass, bool check) 1873{ 1874 auto result = ProfileType(hclass->GetProfileType()); 1875 if (check) { 1876 if (IsSkippableObjectTypeSafe(result)) { 1877 result = ProfileType::PROFILE_TYPE_NONE; 1878 } 1879 } 1880 return result; 1881} 1882 1883void PGOProfiler::ProcessReferences(const WeakRootVisitor &visitor) 1884{ 1885 if (!isEnable_) { 1886 return; 1887 } 1888 preDumpWorkList_.Iterate([this, &visitor](WorkNode *node) { 1889 auto object = reinterpret_cast<TaggedObject *>(node->GetValue()); 1890 auto fwd = visitor(object); 1891 if (fwd == nullptr) { 1892 preDumpWorkList_.Remove(node); 1893 nativeAreaAllocator_->Delete(node); 1894 return; 1895 } 1896 if (fwd != object) { 1897 node->SetValue(JSTaggedType(fwd)); 1898 } 1899 }); 1900} 1901 1902void PGOProfiler::Iterate(const RootVisitor &visitor) 1903{ 1904 if (!isEnable_) { 1905 return; 1906 } 1907 // If the IC of the method is stable, the current design forces the dump data. 1908 // Must pause dump during GC. 1909 dumpWorkList_.Iterate([&visitor](WorkNode* node) { 1910 visitor(Root::ROOT_VM, ObjectSlot(node->GetValueAddr())); 1911 }); 1912} 1913 1914PGOProfiler::PGOProfiler(EcmaVM* vm, bool isEnable) 1915 : nativeAreaAllocator_(std::make_unique<NativeAreaAllocator>()), vm_(vm), isEnable_(isEnable) 1916{ 1917 if (isEnable_) { 1918 recordInfos_ = std::make_unique<PGORecordDetailInfos>(0); 1919 } 1920}; 1921 1922PGOProfiler::~PGOProfiler() 1923{ 1924 Reset(false); 1925} 1926 1927void PGOProfiler::Reset(bool isEnable) 1928{ 1929 LockHolder lock(recordInfoMutex_); 1930 isEnable_ = isEnable; 1931 methodCount_ = 0; 1932 if (recordInfos_) { 1933 recordInfos_->Clear(); 1934 } else { 1935 if (isEnable_) { 1936 recordInfos_ = std::make_unique<PGORecordDetailInfos>(0); 1937 } 1938 } 1939} 1940 1941ApEntityId PGOProfiler::GetMethodAbcId(JSTaggedValue jsMethod) 1942{ 1943 ASSERT(jsMethod.IsMethod()); 1944 CString pfName; 1945 1946 const auto *pf = Method::Cast(jsMethod)->GetJSPandaFile(); 1947 if (pf != nullptr) { 1948 pfName = pf->GetJSPandaFileDesc(); 1949 } 1950 ApEntityId abcId(0); 1951 if (!PGOProfilerManager::GetInstance()->GetPandaFileId(pfName, abcId) && !pfName.empty()) { 1952 LOG_ECMA(ERROR) << "Get method abc id failed. abcName: " << pfName; 1953 } 1954 return abcId; 1955} 1956ApEntityId PGOProfiler::GetMethodAbcId(JSFunction *jsFunction) 1957{ 1958 CString pfName; 1959 auto jsMethod = jsFunction->GetMethod(); 1960 if (jsMethod.IsMethod()) { 1961 return GetMethodAbcId(jsMethod); 1962 } 1963 LOG_ECMA(ERROR) << "Get method abc id failed. Not a method."; 1964 UNREACHABLE(); 1965} 1966 1967ProfileType PGOProfiler::GetRecordProfileType(JSFunction *jsFunction, const CString &recordName) 1968{ 1969 CString pfName; 1970 auto jsMethod = jsFunction->GetMethod(); 1971 if (jsMethod.IsMethod()) { 1972 const auto *pf = Method::Cast(jsMethod)->GetJSPandaFile(); 1973 if (pf != nullptr) { 1974 pfName = pf->GetJSPandaFileDesc(); 1975 } 1976 } 1977 const auto &pf = JSPandaFileManager::GetInstance()->FindJSPandaFile(pfName); 1978 if (pf == nullptr) { 1979 LOG_ECMA(ERROR) << "Get record profile type failed. pf is null, pfName: " << pfName 1980 << ", recordName: " << recordName; 1981 return ProfileType::PROFILE_TYPE_NONE; 1982 } 1983 return GetRecordProfileType(pf, GetMethodAbcId(jsFunction), recordName); 1984} 1985 1986ProfileType PGOProfiler::GetRecordProfileType(ApEntityId abcId, const CString &recordName) 1987{ 1988 CString pfDesc; 1989 PGOProfilerManager::GetInstance()->GetPandaFileDesc(abcId, pfDesc); 1990 const auto &pf = JSPandaFileManager::GetInstance()->FindJSPandaFile(pfDesc); 1991 if (pf == nullptr) { 1992 LOG_ECMA(ERROR) << "Get record profile type failed. pf is null, pfDesc: " << pfDesc 1993 << ", recordName: " << recordName; 1994 return ProfileType::PROFILE_TYPE_NONE; 1995 } 1996 return GetRecordProfileType(pf, abcId, recordName); 1997} 1998 1999ProfileType PGOProfiler::GetRecordProfileType(const std::shared_ptr<JSPandaFile> &pf, ApEntityId abcId, 2000 const CString &recordName) 2001{ 2002 ASSERT(pf != nullptr); 2003 JSRecordInfo *recordInfo = nullptr; 2004 bool hasRecord = pf->CheckAndGetRecordInfo(recordName, &recordInfo); 2005 if (!hasRecord) { 2006 LOG_ECMA(ERROR) << "Get recordInfo failed. recordName: " << recordName; 2007 return ProfileType::PROFILE_TYPE_NONE; 2008 } 2009 ProfileType recordType {0}; 2010 if (pf->IsBundlePack()) { 2011 recordType = CreateRecordProfileType(abcId, ProfileType::RECORD_ID_FOR_BUNDLE); 2012 recordInfos_->GetRecordPool()->Add(recordType, recordName); 2013 return recordType; 2014 } 2015 if (recordInfo->classId != JSPandaFile::CLASSID_OFFSET_NOT_FOUND) { 2016 recordType = CreateRecordProfileType(abcId, recordInfo->classId); 2017 recordInfos_->GetRecordPool()->Add(recordType, recordName); 2018 return recordType; 2019 } 2020 LOG_ECMA(ERROR) << "Invalid classId, skip it. recordName: " << recordName << ", isCjs: " << recordInfo->isCjs 2021 << ", isJson: " << recordInfo->isJson; 2022 return ProfileType::PROFILE_TYPE_NONE; 2023} 2024 2025void PGOProfiler::WorkList::PushBack(WorkNode *node) 2026{ 2027 if (node == nullptr) { 2028 LOG_ECMA(FATAL) << "PGOProfiler::WorkList::PushBack:node is nullptr"; 2029 UNREACHABLE(); 2030 } 2031 if (last_ == nullptr) { 2032 first_ = node; 2033 last_ = node; 2034 } else { 2035 last_->SetNext(node); 2036 node->SetPrev(last_); 2037 last_ = node; 2038 } 2039 node->SetWorkList(this); 2040} 2041 2042PGOProfiler::WorkNode *PGOProfiler::WorkList::PopFront() 2043{ 2044 WorkNode *result = nullptr; 2045 if (first_ != nullptr) { 2046 result = first_; 2047 if (first_->GetNext() != nullptr) { 2048 first_ = first_->GetNext(); 2049 first_->SetPrev(nullptr); 2050 } else { 2051 first_ = nullptr; 2052 last_ = nullptr; 2053 } 2054 result->SetNext(nullptr); 2055 result->SetWorkList(nullptr); 2056 } 2057 return result; 2058} 2059 2060void PGOProfiler::WorkList::Remove(WorkNode *node) 2061{ 2062 if (node->GetPrev() != nullptr) { 2063 node->GetPrev()->SetNext(node->GetNext()); 2064 } 2065 if (node->GetNext() != nullptr) { 2066 node->GetNext()->SetPrev(node->GetPrev()); 2067 } 2068 if (node == first_) { 2069 first_ = node->GetNext(); 2070 } 2071 if (node == last_) { 2072 last_ = node->GetPrev(); 2073 } 2074 node->SetPrev(nullptr); 2075 node->SetNext(nullptr); 2076 node->SetWorkList(nullptr); 2077} 2078 2079void PGOProfiler::WorkList::Iterate(Callback callback) const 2080{ 2081 auto current = first_; 2082 while (current != nullptr) { 2083 auto next = current->GetNext(); 2084 callback(current); 2085 current = next; 2086 } 2087} 2088 2089ProfileType PGOProfiler::CreateRecordProfileType(ApEntityId abcId, ApEntityId classId) 2090{ 2091 return {abcId, classId, ProfileType::Kind::RecordClassId}; 2092} 2093 2094JSTaggedValue PGOProfiler::TryFindKeyInPrototypeChain(TaggedObject *currObj, JSHClass *currHC, JSTaggedValue key) 2095{ 2096 // This is a temporary solution for Instanceof Only! 2097 // Do NOT use this function for other purpose. 2098 if (currHC->IsDictionaryMode()) { 2099 return JSTaggedValue(currHC); 2100 } 2101 while (!JSTaggedValue(currHC).IsUndefinedOrNull()) { 2102 if (LIKELY(!currHC->IsDictionaryMode())) { 2103 int entry = JSHClass::FindPropertyEntry(vm_->GetJSThread(), currHC, key); 2104 if (entry != -1) { 2105 return JSTaggedValue(currHC); 2106 } 2107 } else { 2108 TaggedArray *array = TaggedArray::Cast(JSObject::Cast(currObj)->GetProperties().GetTaggedObject()); 2109 ASSERT(array->IsDictionaryMode()); 2110 NameDictionary *dict = NameDictionary::Cast(array); 2111 int entry = dict->FindEntry(key); 2112 if (entry != -1) { 2113 return JSTaggedValue(currHC); 2114 } 2115 } 2116 currObj = currHC->GetProto().GetTaggedObject(); 2117 if (JSTaggedValue(currObj).IsUndefinedOrNull()) { 2118 break; 2119 } 2120 currHC = currObj->GetClass(); 2121 } 2122 return JSTaggedValue::Undefined(); 2123} 2124void PGOProfiler::InitJITProfiler() 2125{ 2126 jitProfiler_ = new JITProfiler(vm_); 2127 jitProfiler_->InitJITProfiler(); 2128} 2129 2130} // namespace panda::ecmascript::pgo 2131