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