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/compiler/pgo_type/pgo_type_parser.h"
17
18 namespace panda::ecmascript::kungfu {
19 using ProtoTransType = PGOTypeManager::ProtoTransType;
Parse(const BytecodeInfoCollector & collector,const PGOTypeRecorder & typeRecorder,const PGOHClassGenerator & generator,uint32_t methodOffset)20 void BaseParser::Parse(const BytecodeInfoCollector &collector, const PGOTypeRecorder &typeRecorder,
21 const PGOHClassGenerator &generator, uint32_t methodOffset)
22 {
23 const JSPandaFile *jsPandaFile = collector.GetJSPandaFile();
24 const PGOBCInfo *bcInfo = collector.GetPGOBCInfo();
25
26 bcInfo->IterateInfoByType(methodOffset, type_,
27 [this, jsPandaFile, &typeRecorder, &generator, methodOffset](
28 const uint32_t bcIdx, const uint32_t bcOffset, const uint32_t) {
29 auto defType = typeRecorder.GetPGODefOpType(bcOffset);
30 if (defType.IsNone()) {
31 return;
32 }
33
34 PGOTypeLocation loc(jsPandaFile, methodOffset, bcIdx);
35 if (!RecordTypeInfo(defType, loc)) {
36 return;
37 }
38
39 GenerateHClass(generator, loc);
40 });
41 }
42
RecordTypeInfo(const PGODefineOpType & defType,const PGOTypeLocation & loc)43 bool ClassParser::RecordTypeInfo(const PGODefineOpType &defType, const PGOTypeLocation &loc)
44 {
45 auto rootType = defType.GetProfileType();
46 auto ctorPt = defType.GetCtorPt();
47 if (ctorPt.IsNone()) {
48 return false;
49 }
50 PGOSampleType ctorSampleType(ctorPt);
51
52 auto protoPt = defType.GetPrototypePt();
53 if (protoPt.IsNone()) {
54 return false;
55 }
56 PGOSampleType protoSampleType(protoPt);
57
58 ptManager_->RecordLocationToRootType(loc, rootType);
59 auto ctorLoc = loc.ChangeType(PGOTypeLocation::Type::CONSTRUCTOR);
60 ptManager_->RecordLocationToRootType(ctorLoc, ctorPt);
61
62 auto protoLoc = loc.ChangeType(PGOTypeLocation::Type::PROTOTYPE);
63 ptManager_->RecordLocationToRootType(protoLoc, protoPt);
64 return true;
65 }
66
GenerateHClass(const PGOHClassGenerator & generator,const PGOTypeLocation & loc)67 void ClassParser::GenerateHClass(const PGOHClassGenerator &generator, const PGOTypeLocation &loc)
68 {
69 auto thread = ptManager_->GetJSThread();
70 [[maybe_unused]] EcmaHandleScope scope(thread);
71
72 auto rootType = ptManager_->GetRootIdByLocation(loc);
73 PGOSampleType iSampleType(rootType);
74
75 auto ctorLoc = loc.ChangeType(PGOTypeLocation::Type::CONSTRUCTOR);
76 auto ctorPt = ptManager_->GetRootIdByLocation(ctorLoc);
77 PGOSampleType ctorSampleType(ctorPt);
78
79 auto protoLoc = loc.ChangeType(PGOTypeLocation::Type::PROTOTYPE);
80 auto protoPt = ptManager_->GetRootIdByLocation(protoLoc);
81 PGOSampleType protoSampleType(protoPt);
82
83 // testcase: propertyaccessor2.ts. protoSampleType not find desc
84 if (generator.FindHClassLayoutDesc(ctorSampleType) && generator.FindHClassLayoutDesc(protoSampleType)) {
85 generator.GenerateHClass(ctorSampleType);
86 generator.GenerateHClass(protoSampleType);
87
88 auto phValue = ptManager_->QueryHClass(protoPt, protoPt);
89 JSHandle<JSHClass> phclass(thread, phValue);
90 JSHandle<JSObject> prototype = thread->GetEcmaVM()->GetFactory()->NewJSObjectWithInit(phclass);
91 generator.GenerateIHClass(iSampleType, JSHandle<JSTaggedValue>::Cast(prototype));
92 }
93 }
94
RecordTypeInfo(const PGODefineOpType & defType,const PGOTypeLocation & loc)95 bool ArrayParser::RecordTypeInfo(const PGODefineOpType &defType, const PGOTypeLocation &loc)
96 {
97 auto rootType = defType.GetProfileType();
98 if (!rootType.IsArrayLiteralType()) {
99 return false;
100 }
101
102 ElementsKind kind = defType.GetElementsKind();
103 ptManager_->RecordLocationToElementsKind(loc, kind);
104
105 auto traceId = rootType.GetId();
106 // For PGO, we do not care whether an array isPrototype or not.
107 // This type is used at define point, we can use initial array hclass without IsPrototype bit set.
108 auto hclassIdx = ptManager_->GetJSThread()->GetArrayInstanceHClassIndex(kind, false);
109 ptManager_->RecordConstantIndex(traceId, static_cast<uint32_t>(hclassIdx));
110 return true;
111 }
112
RecordTypeInfo(const PGODefineOpType & defType,const PGOTypeLocation & loc)113 bool ObjectLiteralParser::RecordTypeInfo(const PGODefineOpType &defType, const PGOTypeLocation &loc)
114 {
115 auto rootType = defType.GetProfileType();
116 ptManager_->RecordLocationToRootType(loc, rootType);
117 return true;
118 }
119
GenerateHClass(const PGOHClassGenerator & generator,const PGOTypeLocation & loc)120 void ObjectLiteralParser::GenerateHClass(const PGOHClassGenerator &generator, const PGOTypeLocation &loc)
121 {
122 auto thread = ptManager_->GetJSThread();
123 [[maybe_unused]] EcmaHandleScope scope(thread);
124
125 auto rootType = ptManager_->GetRootIdByLocation(loc);
126 PGOSampleType rootSampleType(rootType);
127 generator.SetStatus(PGOHClassGenerator::Status::ISCACHE);
128 generator.GenerateHClass(rootSampleType);
129 }
130
RecordTypeInfo(const PGODefineOpType & defType,const PGOTypeLocation & loc)131 bool FunctionParser::RecordTypeInfo(const PGODefineOpType &defType, const PGOTypeLocation &loc)
132 {
133 auto rootType = defType.GetProfileType();
134 auto ctorPt = defType.GetCtorPt();
135 if (ctorPt.IsNone()) {
136 return false;
137 }
138 PGOSampleType ctorSampleType(ctorPt);
139
140 auto protoPt = defType.GetPrototypePt();
141 if (protoPt.IsNone()) {
142 return false;
143 }
144 PGOSampleType protoSampleType(protoPt);
145
146 ptManager_->RecordLocationToRootType(loc, rootType);
147 auto ctorLoc = loc.ChangeType(PGOTypeLocation::Type::CONSTRUCTOR);
148 ptManager_->RecordLocationToRootType(ctorLoc, ctorPt);
149
150 auto protoLoc = loc.ChangeType(PGOTypeLocation::Type::PROTOTYPE);
151 ptManager_->RecordLocationToRootType(protoLoc, protoPt);
152 return true;
153 }
154
GenerateHClass(const PGOHClassGenerator & generator,const PGOTypeLocation & loc)155 void FunctionParser::GenerateHClass(const PGOHClassGenerator &generator, const PGOTypeLocation &loc)
156 {
157 auto thread = ptManager_->GetJSThread();
158 [[maybe_unused]] EcmaHandleScope scope(thread);
159
160 auto rootType = ptManager_->GetRootIdByLocation(loc);
161 PGOSampleType iSampleType(rootType);
162
163 auto ctorLoc = loc.ChangeType(PGOTypeLocation::Type::CONSTRUCTOR);
164 auto ctorPt = ptManager_->GetRootIdByLocation(ctorLoc);
165 PGOSampleType ctorSampleType(ctorPt);
166
167 auto protoLoc = loc.ChangeType(PGOTypeLocation::Type::PROTOTYPE);
168 auto protoPt = ptManager_->GetRootIdByLocation(protoLoc);
169 PGOSampleType protoSampleType(protoPt);
170
171 // testcase: propertyaccessor2.ts. protoSampleType not find desc
172 if (generator.FindHClassLayoutDesc(ctorSampleType) && generator.FindHClassLayoutDesc(protoSampleType)) {
173 generator.GenerateHClass(protoSampleType);
174
175 auto phValue = ptManager_->QueryHClass(protoPt, protoPt);
176 JSHandle<JSHClass> phclass(thread, phValue);
177 JSHandle<JSObject> prototype = thread->GetEcmaVM()->GetFactory()->NewJSObjectWithInit(phclass);
178 generator.GenerateIHClass(iSampleType, JSHandle<JSTaggedValue>::Cast(prototype));
179 }
180 }
181
PGOTypeParser(const PGOProfilerDecoder & decoder,PGOTypeManager * ptManager)182 PGOTypeParser::PGOTypeParser(const PGOProfilerDecoder &decoder, PGOTypeManager *ptManager)
183 : decoder_(decoder), ptManager_(ptManager)
184 {
185 parsers_.emplace_back(std::make_unique<EmptyArrayParser>(ptManager));
186 parsers_.emplace_back(std::make_unique<ArrayLiteralParser>(ptManager));
187 parsers_.emplace_back(std::make_unique<ObjectLiteralParser>(ptManager));
188 }
189
SkipGenerateHClass(PGOTypeRecorder typeRecorder,ProfileType rootType,bool isCache,PGOHClassTreeDesc * desc)190 bool PGOTypeParser::SkipGenerateHClass(PGOTypeRecorder typeRecorder, ProfileType rootType,
191 bool isCache, PGOHClassTreeDesc *desc)
192 {
193 // If the definition point is not includes in the AOT list. This hclass needs to be discarded.
194 // otherwise, the using point must to be deopt.
195 if ((!rootType.IsNapiType() && !isCache && !typeRecorder.IsValidPt(rootType)) ||
196 desc->CheckHasInvalidType()) {
197 return true;
198 }
199 return false;
200 }
201
CreatePGOType(BytecodeInfoCollector & collector)202 void PGOTypeParser::CreatePGOType(BytecodeInfoCollector &collector)
203 {
204 const JSPandaFile *jsPandaFile = collector.GetJSPandaFile();
205 PGOTypeRecorder typeRecorder(decoder_);
206 typeRecorder.IterateProtoTransitionPool([this](PGOProtoTransitionType type) {
207 ProfileType ihcType = type.GetIhcType();
208 std::pair<ProfileType, ProfileType> baseType = type.GetBaseType();
209 ProfileType transIhcType = type.GetTransitionType();
210 ProfileType transPhcType = type.GetTransitionProtoPt();
211 ProtoTransType transType(ihcType, baseType.first, baseType.second, transIhcType, transPhcType);
212 ptManager_->RecordProtoTransType(transType);
213 });
214
215 typeRecorder.IterateHClassTreeDesc([this, typeRecorder](PGOHClassTreeDesc *desc) {
216 auto rootType = desc->GetProfileType();
217 auto protoPt = desc->GetProtoPt();
218 bool isCache = rootType.IsObjectLiteralType();
219 if (SkipGenerateHClass(typeRecorder, rootType, isCache, desc)) {
220 return ;
221 }
222 PGOHClassGenerator::Status status = PGOHClassGenerator::Status::NONE;
223 if (isCache) {
224 status = PGOHClassGenerator::Status::ISCACHE;
225 }
226 const PGOHClassGenerator generator(typeRecorder, ptManager_, status);
227 if (rootType.IsNapiType()) {
228 this->GenerateHClassForNapiType(rootType, generator);
229 } else if (rootType.IsGeneralizedClassType()) {
230 this->GenerateHClassForClassType(rootType, protoPt, generator);
231 } else if (rootType.IsPrototype()) {
232 this->GenerateHClassForPrototype(rootType, generator);
233 } else {
234 generator.GenerateHClass(PGOSampleType(rootType));
235 }
236 });
237
238 collector.IterateAllMethods([this, jsPandaFile, &collector](uint32_t methodOffset) {
239 PGOTypeRecorder typeRecorder(decoder_, jsPandaFile, methodOffset);
240 const PGOHClassGenerator generator(typeRecorder, ptManager_);
241 for (auto &parser : parsers_) {
242 parser->Parse(collector, typeRecorder, generator, methodOffset);
243 }
244 });
245 }
246
GenerateHClassForNapiType(ProfileType rootType,const PGOHClassGenerator & generator)247 void PGOTypeParser::GenerateHClassForNapiType(ProfileType rootType, const PGOHClassGenerator &generator)
248 {
249 auto thread = ptManager_->GetJSThread();
250 JSHandle<JSTaggedValue> nullhandle(thread, JSTaggedValue::Null());
251 PGOSampleType rootSampleType(rootType);
252 generator.GenerateIHClass(rootSampleType, nullhandle);
253 }
254
GenerateHClassForClassType(ProfileType rootType,ProfileType protoPt,const PGOHClassGenerator & generator)255 void PGOTypeParser::GenerateHClassForClassType(ProfileType rootType, ProfileType protoPt,
256 const PGOHClassGenerator &generator)
257 {
258 if (!protoPt.IsGeneralizedPrototype()) {
259 return;
260 }
261 auto phValue = ptManager_->QueryHClass(protoPt, protoPt);
262 if (phValue.IsUndefined()) {
263 generator.GenerateHClass(PGOSampleType(protoPt));
264 }
265 phValue = ptManager_->QueryHClass(protoPt, protoPt);
266 if (phValue.IsUndefined()) {
267 return;
268 }
269 ASSERT(phValue.IsJSHClass());
270 auto thread = ptManager_->GetJSThread();
271 JSHandle<JSHClass> phclass(thread, phValue);
272 JSHandle<JSObject> prototype = thread->GetEcmaVM()->GetFactory()->NewJSObjectWithInit(phclass);
273 PGOSampleType rootSampleType(rootType);
274 generator.GenerateIHClass(rootSampleType, JSHandle<JSTaggedValue>::Cast(prototype));
275 }
276
GenerateHClassForPrototype(ProfileType rootType,const PGOHClassGenerator & generator)277 void PGOTypeParser::GenerateHClassForPrototype(ProfileType rootType, const PGOHClassGenerator &generator)
278 {
279 PGOSampleType rootSampleType(rootType);
280 // When the collected object only has phc, use phc to create a prototype and store it in the IHC field.
281 generator.GenerateHClass(rootSampleType);
282 auto classType = ProfileType(rootType.GetRaw());
283 classType.UpdateKind(ProfileType::Kind::ClassId);
284 auto ihc = ptManager_->QueryHClass(classType, classType);
285 if (ihc.IsUndefined()) {
286 auto phc = ptManager_->QueryHClass(rootType, rootType);
287 auto thread = ptManager_->GetJSThread();
288 JSHandle<JSHClass> phclass(thread, phc);
289 if (!phc.IsUndefined()) {
290 JSHandle<JSObject> prototype = thread->GetEcmaVM()->GetFactory()->NewJSObjectWithInit(phclass);
291 ptManager_->RecordHClass(classType, classType, prototype.GetTaggedType());
292 }
293 }
294 }
295 } // namespace panda::ecmascript
296