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