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