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