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