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