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