• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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