1 /*
2 * Copyright (c) 2023-2024 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/compiler/pgo_type/pgo_type_manager.h"
17
18 #include "ecmascript/jspandafile/program_object.h"
19 #include "ecmascript/layout_info-inl.h"
20 namespace panda::ecmascript::kungfu {
Iterate(RootVisitor & v)21 void PGOTypeManager::Iterate(RootVisitor &v)
22 {
23 for (auto &iter : hcData_) {
24 for (auto &hclassIter : iter.second) {
25 v.VisitRoot(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&(hclassIter.second))));
26 }
27 }
28 aotSnapshot_.Iterate(v);
29 for (auto &iter : hclassInfoLocal_) {
30 if (iter.GetRawData() != 0) {
31 v.VisitRoot(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&(iter))));
32 }
33 }
34 }
35
GetConstantPoolIDByMethodOffset(const uint32_t methodOffset) const36 uint32_t PGOTypeManager::GetConstantPoolIDByMethodOffset(const uint32_t methodOffset) const
37 {
38 ASSERT(curJSPandaFile_!=nullptr);
39 panda_file::IndexAccessor indexAccessor(*curJSPandaFile_->GetPandaFile(),
40 panda_file::File::EntityId(methodOffset));
41 return static_cast<uint32_t>(indexAccessor.GetHeaderIndex());
42 }
43
GetConstantPoolByMethodOffset(const uint32_t methodOffset) const44 JSTaggedValue PGOTypeManager::GetConstantPoolByMethodOffset(const uint32_t methodOffset) const
45 {
46 uint32_t cpId = GetConstantPoolIDByMethodOffset(methodOffset);
47 return thread_->GetEcmaVM()->FindConstpool(curJSPandaFile_, cpId);
48 }
49
GetStringFromConstantPool(const uint32_t methodOffset,const uint16_t cpIdx) const50 JSTaggedValue PGOTypeManager::GetStringFromConstantPool(const uint32_t methodOffset, const uint16_t cpIdx) const
51 {
52 JSTaggedValue cp = GetConstantPoolByMethodOffset(methodOffset);
53 return ConstantPool::GetStringFromCache(thread_, cp, cpIdx);
54 }
55
InitAOTSnapshot(uint32_t compileFilesCount)56 void PGOTypeManager::InitAOTSnapshot(uint32_t compileFilesCount)
57 {
58 aotSnapshot_.InitSnapshot(compileFilesCount);
59 GenHClassInfo();
60 GenSymbolInfo();
61 GenArrayInfo();
62 GenConstantIndexInfo();
63 GenProtoTransitionInfo();
64 }
65
GetSymbolCountFromHClassData()66 uint32_t PGOTypeManager::GetSymbolCountFromHClassData()
67 {
68 uint32_t count = 0;
69 for (auto& root: hcData_) {
70 for (auto& child: root.second) {
71 if (!JSTaggedValue(child.second).IsJSHClass()) {
72 continue;
73 }
74 JSHClass* hclass = JSHClass::Cast(JSTaggedValue(child.second).GetTaggedObject());
75 if (!hclass->HasTransitions(thread_)) {
76 LayoutInfo* layoutInfo = LayoutInfo::GetLayoutInfoFromHClass(thread_, hclass);
77 uint32_t len = hclass->NumberOfProps();
78 for (uint32_t i = 0; i < len; i++) {
79 JSTaggedValue key = layoutInfo->GetKey(thread_, i);
80 if (key.IsSymbol() && JSSymbol::Cast(key)->HasId()) {
81 count++;
82 }
83 }
84 }
85 }
86 }
87 return count;
88 }
89
GenSymbolInfo()90 void PGOTypeManager::GenSymbolInfo()
91 {
92 uint32_t count = GetSymbolCountFromHClassData();
93 ObjectFactory* factory = thread_->GetEcmaVM()->GetFactory();
94 JSHandle<TaggedArray> symbolInfo = factory->NewTaggedArray(count * 2); // 2: symbolId, symbol
95 uint32_t pos = 0;
96
97 for (auto& root: hcData_) {
98 ProfileType rootType = root.first;
99 for (auto& child: root.second) {
100 if (!JSTaggedValue(child.second).IsJSHClass()) {
101 continue;
102 }
103 ProfileType childType = child.first;
104 JSHClass* hclass = JSHClass::Cast(JSTaggedValue(child.second).GetTaggedObject());
105 if (!hclass->HasTransitions(thread_)) {
106 LayoutInfo* layoutInfo = LayoutInfo::GetLayoutInfoFromHClass(thread_, hclass);
107 uint32_t len = hclass->NumberOfProps();
108 for (uint32_t i = 0; i < len; i++) {
109 JSTaggedValue symbol = layoutInfo->GetKey(thread_, i);
110 if (symbol.IsSymbol() && JSSymbol::Cast(symbol)->HasId()) {
111 JSSymbol* symbolPtr = JSSymbol::Cast(symbol.GetTaggedObject());
112 uint64_t symbolId = symbolPtr->GetPrivateId();
113 uint64_t slotIndex = JSSymbol::GetSlotIndex(symbolId);
114 ProfileTypeTuple symbolIdKey = std::make_tuple(rootType, childType, slotIndex);
115 profileTypeToSymbolId_.emplace(symbolIdKey, symbolId);
116 symbolInfo->Set(thread_, pos++, JSTaggedValue(symbolId));
117 symbolInfo->Set(thread_, pos++, symbol);
118 }
119 }
120 }
121 }
122 }
123
124 EcmaVM *vm = thread_->GetEcmaVM();
125 if (vm->GetJSOptions().IsEnableCompilerLogSnapshot()) {
126 vm->AddAOTSnapShotStats("SymbolInfo", symbolInfo->GetLength());
127 }
128 aotSnapshot_.StoreSymbolInfo(symbolInfo);
129 }
130
IsNapiIhc(ProfileType rootType,ProfileType childType)131 bool PGOTypeManager::IsNapiIhc(ProfileType rootType, ProfileType childType)
132 {
133 // If the rootType is NapiType and the childType is root, it must be napi object's ihc.
134 return rootType.IsNapiType() && childType.IsRootType();
135 }
136
137 /* hclassInfo
138 * +-----------------------------------+----
139 * | AOT Napi Hclass | zero slot
140 * +-----------------------------------+----
141 * | ... | ^
142 * | ... | |
143 * | hcData | pos
144 * | ... | |
145 * | ... | v
146 * +-----------------------------------+----
147 * | Object Literal Cache (maybe hole) | last slot
148 * +-----------------------------------+----
149 */
150
GenHClassInfo()151 void PGOTypeManager::GenHClassInfo()
152 {
153 uint32_t count = 2; // For object literal hclass cache and Napi root type.
154 for (auto& root: hcData_) {
155 count += root.second.size();
156 }
157
158 ObjectFactory* factory = thread_->GetEcmaVM()->GetFactory();
159 JSHandle<TaggedArray> hclassInfo = factory->NewTaggedArray(count);
160 uint32_t pos = 1; // 1: the first one is the napi object's ihc.
161 profileTyperToHClassIndex_.clear();
162 for (auto& root: hcData_) {
163 ProfileType rootType = root.first;
164 for (auto& child: root.second) {
165 ProfileType childType = child.first;
166 JSTaggedType hclass = child.second;
167 ProfileTyper key = std::make_pair(rootType, childType);
168 if (IsNapiIhc(rootType, childType)) {
169 profileTyperToHClassIndex_.emplace(key, 0);
170 hclassInfo->Set(thread_, 0, JSTaggedValue(hclass));
171 } else {
172 profileTyperToHClassIndex_.emplace(key, pos);
173 hclassInfo->Set(thread_, pos++, JSTaggedValue(hclass));
174 }
175 }
176 }
177 // The cache of Object literal serializes to last index of AOTHClassInfo.
178 JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
179 JSHandle<JSTaggedValue> maybeCache = env->GetObjectLiteralHClassCache();
180 if (!maybeCache->IsHole()) {
181 // It cannot be serialized if object in global env.
182 JSHandle<TaggedArray> array(maybeCache);
183 auto cloneResult = factory->CopyArray(array, array->GetLength(), array->GetLength());
184 hclassInfo->Set(thread_, hclassInfo->GetLength() - 1, cloneResult);
185 }
186
187 EcmaVM *vm = thread_->GetEcmaVM();
188 if (vm->GetJSOptions().IsEnableCompilerLogSnapshot()) {
189 vm->AddAOTSnapShotStats("HClassInfo", hclassInfo->GetLength());
190 }
191 aotSnapshot_.StoreHClassInfo(hclassInfo);
192 }
193
GenProtoTransitionInfo()194 void PGOTypeManager::GenProtoTransitionInfo()
195 {
196 auto transitionTable = thread_->GetEcmaVM()->GetFunctionProtoTransitionTable();
197 for (auto &protoTransType : protoTransTypes_) {
198 JSTaggedValue ihc = QueryHClass(protoTransType.ihcType, protoTransType.ihcType);
199 JSTaggedValue baseIhc = QueryHClass(protoTransType.baseRootType, protoTransType.baseType);
200 JSTaggedValue transIhc = QueryHClass(protoTransType.transIhcType, protoTransType.transIhcType);
201 JSTaggedValue transPhc = QueryHClass(protoTransType.transPhcType, protoTransType.transPhcType);
202 if (ihc.IsUndefined() || baseIhc.IsUndefined() || transIhc.IsUndefined() || transPhc.IsUndefined()) {
203 LOG_COMPILER(DEBUG) << "broken prototype transition info!";
204 continue;
205 }
206 transitionTable->InsertTransitionItem(thread_,
207 JSHandle<JSTaggedValue>(thread_, ihc),
208 JSHandle<JSTaggedValue>(thread_, baseIhc),
209 JSHandle<JSTaggedValue>(thread_, transIhc),
210 JSHandle<JSTaggedValue>(thread_, transPhc));
211 // Situation:
212 // 1: d1.prototype = p
213 // 2: d2.prototype = p
214 // For this case, baseIhc is transPhc
215 transitionTable->InsertTransitionItem(thread_,
216 JSHandle<JSTaggedValue>(thread_, ihc),
217 JSHandle<JSTaggedValue>(thread_, transPhc),
218 JSHandle<JSTaggedValue>(thread_, transIhc),
219 JSHandle<JSTaggedValue>(thread_, transPhc));
220 }
221 aotSnapshot_.StoreProtoTransTableInfo(JSHandle<JSTaggedValue>(thread_, transitionTable->GetProtoTransitionTable()));
222 }
223
GenArrayInfo()224 void PGOTypeManager::GenArrayInfo()
225 {
226 EcmaVM *vm = thread_->GetEcmaVM();
227 ObjectFactory *factory = vm->GetFactory();
228 JSHandle<TaggedArray> arrayInfo = factory->EmptyArray();
229 if (vm->GetJSOptions().IsEnableCompilerLogSnapshot()) {
230 vm->AddAOTSnapShotStats("ArrayInfo", arrayInfo->GetLength());
231 }
232 aotSnapshot_.StoreArrayInfo(arrayInfo);
233 }
234
GenConstantIndexInfo()235 void PGOTypeManager::GenConstantIndexInfo()
236 {
237 uint32_t count = constantIndexData_.size();
238 EcmaVM *vm = thread_->GetEcmaVM();
239 ObjectFactory *factory = vm->GetFactory();
240 JSHandle<TaggedArray> constantIndexInfo = factory->NewTaggedArray(count);
241 for (uint32_t pos = 0; pos < count; pos++) {
242 constantIndexInfo->Set(thread_, pos, JSTaggedValue(constantIndexData_[pos]));
243 }
244 if (vm->GetJSOptions().IsEnableCompilerLogSnapshot()) {
245 vm->AddAOTSnapShotStats("ConstantIndexInfo", constantIndexInfo->GetLength());
246 }
247 aotSnapshot_.StoreConstantIndexInfo(constantIndexInfo);
248 }
249
RecordHClass(ProfileType rootType,ProfileType childType,JSTaggedType hclass,bool update)250 void PGOTypeManager::RecordHClass(ProfileType rootType, ProfileType childType, JSTaggedType hclass, bool update)
251 {
252 LockHolder lock(mutex_);
253 auto iter = hcData_.find(rootType);
254 if (iter == hcData_.end()) {
255 auto map = TransIdToHClass();
256 map.emplace(childType, hclass);
257 hcData_.emplace(rootType, map);
258 return;
259 }
260
261 auto &hclassMap = iter->second;
262 auto hclassIter = hclassMap.find(childType);
263 if (hclassIter != hclassMap.end()) {
264 if (!update) {
265 ASSERT(hclass == hclassIter->second);
266 return;
267 } else {
268 hclassMap[childType]= hclass;
269 return;
270 }
271 }
272 hclassMap.emplace(childType, hclass);
273 }
274
RecordConstantIndex(uint32_t bcAbsoluteOffset,uint32_t index)275 void PGOTypeManager::RecordConstantIndex(uint32_t bcAbsoluteOffset, uint32_t index)
276 {
277 constantIndexData_.emplace_back(bcAbsoluteOffset);
278 constantIndexData_.emplace_back(index);
279 }
280
GetHClassIndexByProfileType(ProfileTyper type) const281 uint32_t PGOTypeManager::GetHClassIndexByProfileType(ProfileTyper type) const
282 {
283 uint32_t index = -1;
284 auto iter = profileTyperToHClassIndex_.find(type);
285 if (iter != profileTyperToHClassIndex_.end()) {
286 index = iter->second;
287 }
288 return index;
289 }
290
GetHolderHIndexByPGOObjectInfoType(pgo::PGOObjectInfo type,bool isAot)291 int PGOTypeManager::GetHolderHIndexByPGOObjectInfoType(pgo::PGOObjectInfo type, bool isAot)
292 {
293 if (isAot) {
294 ProfileTyper holderType = std::make_pair(type.GetHoldRootType(), type.GetHoldType());
295 int holderHCIndex = static_cast<int>(GetHClassIndexByProfileType(holderType));
296 ASSERT(QueryHClass(holderType.first, holderType.second).IsJSHClass());
297 return holderHCIndex;
298 } else {
299 JSHClass *holderHClass = type.GetHolderHclass();
300 int holderHCIndex = RecordAndGetHclassIndexForJIT(holderHClass);
301 return holderHCIndex;
302 }
303 }
304
GetReceiverHIndexByPGOObjectInfoType(pgo::PGOObjectInfo type,bool isAot)305 int PGOTypeManager::GetReceiverHIndexByPGOObjectInfoType(pgo::PGOObjectInfo type, bool isAot)
306 {
307 if (isAot) {
308 ProfileTyper receiverType = std::make_pair(type.GetReceiverRootType(), type.GetReceiverType());
309 int receiverHCIndex = static_cast<int>(GetHClassIndexByProfileType(receiverType));
310 ASSERT(QueryHClass(receiverType.first, receiverType.second).IsJSHClass());
311 return receiverHCIndex;
312 } else {
313 JSHClass *receiverHClass = type.GetReceiverHclass();
314 int receiverHCIndex = RecordAndGetHclassIndexForJIT(receiverHClass);
315 return receiverHCIndex;
316 }
317 }
318
GetSymbolIdByProfileType(ProfileTypeTuple type) const319 std::optional<uint64_t> PGOTypeManager::GetSymbolIdByProfileType(ProfileTypeTuple type) const
320 {
321 auto iter = profileTypeToSymbolId_.find(type);
322 if (iter != profileTypeToSymbolId_.end()) {
323 return iter->second;
324 }
325 return std::nullopt;
326 }
327
QueryHClass(ProfileType rootType,ProfileType childType)328 JSTaggedValue PGOTypeManager::QueryHClass(ProfileType rootType, ProfileType childType)
329 {
330 LockHolder lock(mutex_);
331 JSTaggedValue result = JSTaggedValue::Undefined();
332 auto iter = hcData_.find(rootType);
333 if (iter != hcData_.end()) {
334 auto hclassMap = iter->second;
335 auto hclassIter = hclassMap.find(childType);
336 if (hclassIter != hclassMap.end()) {
337 result = JSTaggedValue(hclassIter->second);
338 }
339 }
340 // Can return real hclass of object or prototype of it
341 return result;
342 }
343
QueryHClassByIndexForJIT(uint32_t hclassIndex)344 JSTaggedValue PGOTypeManager::QueryHClassByIndexForJIT(uint32_t hclassIndex)
345 {
346 ASSERT(hclassIndex < pos_);
347 return hclassInfoLocal_[hclassIndex];
348 }
349
RecordAndGetHclassIndexForJIT(JSHClass * hclass)350 int PGOTypeManager::RecordAndGetHclassIndexForJIT(JSHClass* hclass)
351 {
352 // The hclass in hclassInfoLocal_ cannot currently be cleared after each
353 // JIT session because it depends on ensuring the survival of hclass.
354 // If a reference from machineCode to hclass is added in the future, then it can be cleared.
355 auto value = JSTaggedValue::Cast(hclass);
356 for (int i = 0; i < pos_; i++) {
357 if (hclassInfoLocal_[i] == value) {
358 return i;
359 }
360 }
361 hclassInfoLocal_.emplace_back(value);
362 return pos_++;
363 }
364
MergeRepresentationForProtoTransition()365 void PGOTypeManager::MergeRepresentationForProtoTransition()
366 {
367 for (auto &protoTransType : protoTransTypes_) {
368 JSTaggedValue ihc = QueryHClass(protoTransType.ihcType, protoTransType.ihcType);
369 JSTaggedValue baseIhc = QueryHClass(protoTransType.baseRootType, protoTransType.baseType);
370 JSTaggedValue transIhc = QueryHClass(protoTransType.transIhcType, protoTransType.transIhcType);
371 JSTaggedValue transPhc = QueryHClass(protoTransType.transPhcType, protoTransType.transPhcType);
372 if (ihc.IsUndefined() || baseIhc.IsUndefined() || transIhc.IsUndefined() || transPhc.IsUndefined()) {
373 LOG_COMPILER(DEBUG) << "broken prototype transition info!";
374 continue;
375 }
376 JSHClass::MergeRepresentation(thread_, JSHClass::Cast(baseIhc.GetTaggedObject()),
377 JSHClass::Cast(transPhc.GetTaggedObject()));
378 JSHClass::MergeRepresentation(thread_, JSHClass::Cast(ihc.GetTaggedObject()),
379 JSHClass::Cast(transIhc.GetTaggedObject()));
380 }
381 }
382 } // namespace panda::ecmascript
383