1 /*
2 * Copyright (c) 2022 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/type_recorder.h"
17
18 #include "ecmascript/compiler/ts_hclass_generator.h"
19 #include "ecmascript/jspandafile/type_literal_extractor.h"
20 #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
21 #include "ecmascript/pgo_profiler/pgo_profiler_layout.h"
22 #include "ecmascript/pgo_profiler/pgo_profiler_type.h"
23 #include "ecmascript/ts_types/ts_type_parser.h"
24
25 namespace panda::ecmascript::kungfu {
TypeRecorder(const JSPandaFile * jsPandaFile,const MethodLiteral * methodLiteral,TSManager * tsManager,const CString & recordName,PGOProfilerDecoder * decoder,const MethodPcInfo & methodPCInfo,const Bytecodes * bytecodes,bool enableOptTrackField)26 TypeRecorder::TypeRecorder(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral,
27 TSManager *tsManager, const CString &recordName, PGOProfilerDecoder *decoder,
28 const MethodPcInfo &methodPCInfo, const Bytecodes *bytecodes, bool enableOptTrackField)
29 : argTypes_(methodLiteral->GetNumArgsWithCallField() + static_cast<size_t>(TypedArgIdx::NUM_OF_TYPED_ARGS),
30 GateType::AnyType()), decoder_(decoder), enableOptTrackField_(enableOptTrackField),
31 pcOffsets_(methodPCInfo.pcOffsets), bytecodes_(bytecodes)
32 {
33 TSHClassGenerator generator(tsManager);
34 if (jsPandaFile->HasTSTypes(recordName)) {
35 LoadTypes(jsPandaFile, methodLiteral, tsManager, recordName);
36 }
37 LoadTypesFromPGO(jsPandaFile, methodLiteral, recordName);
38 CreateTypesForPGO(jsPandaFile, methodLiteral, tsManager, recordName);
39 generator.GenerateTSHClasses();
40 }
41
LoadTypes(const JSPandaFile * jsPandaFile,const MethodLiteral * methodLiteral,TSManager * tsManager,const CString & recordName)42 void TypeRecorder::LoadTypes(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral,
43 TSManager *tsManager, const CString &recordName)
44 {
45 TSTypeParser typeParser(tsManager);
46 panda_file::File::EntityId fieldId = methodLiteral->GetMethodId();
47 uint32_t methodOffset = fieldId.GetOffset();
48 TypeAnnotationExtractor annoExtractor(jsPandaFile, methodOffset);
49 GlobalTSTypeRef funcGT = typeParser.CreateGT(jsPandaFile, recordName, annoExtractor.GetMethodTypeOffset());
50 GlobalTSTypeRef thisGT;
51 annoExtractor.EnumerateInstsAndTypes([this, &typeParser, &jsPandaFile, &recordName,
52 &thisGT, tsManager, methodOffset](const int32_t bcIdx, const uint32_t typeId) {
53 GlobalTSTypeRef gt = typeParser.CreateGT(jsPandaFile, recordName, typeId);
54 if (TypeNeedFilter(gt)) {
55 return;
56 }
57 TypeLocation loc(jsPandaFile, methodOffset, bcIdx);
58 CollectLiteralGT(tsManager, loc, gt);
59
60 // The type of a function is recorded as (-1, funcTypeId). If the function is a member of a class,
61 // the type of the class or its instance is is recorded as (-2, classTypeId). If it is a static
62 // member, the type id refers to the type of the class; otherwise, it links to the type of the
63 // instances of the class.
64 if (bcIdx == METHOD_ANNOTATION_THIS_TYPE_OFFSET) {
65 thisGT = gt;
66 return;
67 }
68 auto type = GateType(gt);
69 bcOffsetGtMap_.emplace(bcIdx, type);
70 });
71 const auto &methodList = typeParser.GetMethodList();
72 auto methodIter = methodList.find(methodOffset);
73 if (methodIter != methodList.end()) {
74 const auto &methodInfo = methodIter->second;
75 const auto &bcTypes = methodInfo.GetBCAndTypes();
76 for (const auto &pair : bcTypes) {
77 GlobalTSTypeRef gt = typeParser.CreateGT(jsPandaFile, recordName, pair.second);
78 // if the function type has already recorded in the next pc, we should skip it.
79 if (CheckTypeMarkForDefineFunc(pair.first)) {
80 continue;
81 }
82 bcOffsetGtMap_.emplace(pair.first, GateType(gt));
83 }
84 }
85 LoadArgTypes(tsManager, funcGT, thisGT);
86 }
87
CollectLiteralGT(TSManager * tsManager,TypeLocation & loc,GlobalTSTypeRef gt)88 void TypeRecorder::CollectLiteralGT(TSManager *tsManager, TypeLocation &loc, GlobalTSTypeRef gt)
89 {
90 int32_t bcIdx = loc.GetBcIdx();
91 if (bcIdx < 0) {
92 return;
93 }
94
95 if (bytecodes_->GetOpcode(pcOffsets_[bcIdx]) == EcmaOpcode::STA_V8) {
96 // bcIndex of literal marked in es2abc maybe in the next bc whose opcode should be sta.
97 bcIdx--;
98 loc.SetBcIdx(bcIdx);
99 }
100
101 EcmaOpcode ecmaOpcode = bytecodes_->GetOpcode(pcOffsets_[bcIdx]);
102
103 switch (ecmaOpcode) {
104 case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8:
105 case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8: {
106 if (tsManager->IsUserDefinedClassTypeKind(gt)) {
107 tsManager->InsertLiteralGTMap(loc, gt);
108 }
109 return;
110 }
111 case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM8_ID16:
112 case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: {
113 if (tsManager->IsObjectTypeKind(gt)) {
114 tsManager->InsertLiteralGTMap(loc, gt);
115 }
116 return;
117 }
118 default:
119 return;
120 }
121 }
122
CheckTypeMarkForDefineFunc(uint32_t checkBc) const123 bool TypeRecorder::CheckTypeMarkForDefineFunc(uint32_t checkBc) const
124 {
125 // bcOffset of definefunc marked in es2abc maybe in the next bc whose opcode should be sta.
126 uint32_t staBc = checkBc + 1;
127 return bcOffsetGtMap_.find(staBc) != bcOffsetGtMap_.end() &&
128 staBc < pcOffsets_.size() && bytecodes_->GetOpcode(pcOffsets_[staBc]) == EcmaOpcode::STA_V8;
129 }
130
LoadTypesFromPGO(const JSPandaFile * jsPandaFile,const MethodLiteral * methodLiteral,const CString & recordName)131 void TypeRecorder::LoadTypesFromPGO(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral,
132 const CString &recordName)
133 {
134 auto callback = [this] (uint32_t offset, PGOType *type) {
135 if (type->IsScalarOpType()) {
136 bcOffsetPGOOpTypeMap_[offset] = *reinterpret_cast<PGOSampleType *>(type);
137 } else if (type->IsRwOpType()) {
138 bcOffsetPGORwTypeMap_[offset] = *reinterpret_cast<PGORWOpType *>(type);
139 } else {
140 UNREACHABLE();
141 }
142 };
143 if (decoder_ != nullptr) {
144 decoder_->GetTypeInfo(jsPandaFile, recordName, methodLiteral, callback);
145 }
146 }
147
CreateTypesForPGO(const JSPandaFile * jsPandaFile,const MethodLiteral * methodLiteral,TSManager * tsManager,const CString & recordName)148 void TypeRecorder::CreateTypesForPGO(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral,
149 TSManager *tsManager, const CString &recordName)
150 {
151 TSTypeParser typeParser(tsManager);
152 uint32_t methodOffset = methodLiteral->GetMethodId().GetOffset();
153 PGOBCInfo *bcInfo = tsManager->GetBytecodeInfoCollector()->GetPGOBCInfo();
154 bcInfo->IterateInfoAndType(methodOffset, [this, &typeParser, methodOffset, &recordName, &jsPandaFile, tsManager]
155 (const PGOBCInfo::Type type, const uint32_t bcIdx, const uint32_t bcOffset, const uint32_t cpIdx) {
156 auto it = bcOffsetPGOOpTypeMap_.find(bcOffset);
157 if (it == bcOffsetPGOOpTypeMap_.end()) {
158 return;
159 }
160
161 EcmaOpcode ecmaOpcode = bytecodes_->GetOpcode(pcOffsets_[bcIdx]);
162 if (jsPandaFile->HasTSTypes(recordName) && Bytecodes::IsCallOp(ecmaOpcode)) {
163 int32_t callTargetMethodOffset = it->second.GetClassType().GetClassType();
164 if (callTargetMethodOffset == 0) {
165 return;
166 }
167 TypeAnnotationExtractor annoExtractor(jsPandaFile, callTargetMethodOffset);
168 GlobalTSTypeRef funcGT =
169 typeParser.CreateGT(jsPandaFile, recordName, annoExtractor.GetMethodTypeOffset());
170 if (funcGT.IsDefault()) {
171 return;
172 }
173 GateType callTargetType = GateType(funcGT);
174 bcOffsetCallTargetGtMap_.emplace(bcIdx, callTargetType);
175 return;
176 }
177
178 TypeLocation loc(jsPandaFile, methodOffset, bcIdx);
179 if (!tsManager->GetLiteralGT(loc).IsDefault()) {
180 return;
181 }
182
183 GlobalTSTypeRef gt = typeParser.CreatePGOGT(TSTypeParser::PGOInfo {
184 jsPandaFile, recordName, methodOffset, cpIdx, it->second, type, decoder_, enableOptTrackField_ });
185 if (TypeNeedFilter(gt)) {
186 return;
187 }
188 CollectLiteralGT(tsManager, loc, gt);
189 GateType gateType = GateType(gt);
190 bcOffsetGtMap_.emplace(bcIdx, gateType);
191 });
192 }
193
LoadArgTypes(const TSManager * tsManager,GlobalTSTypeRef funcGT,GlobalTSTypeRef thisGT)194 void TypeRecorder::LoadArgTypes(const TSManager *tsManager, GlobalTSTypeRef funcGT, GlobalTSTypeRef thisGT)
195 {
196 argTypes_[static_cast<size_t>(TypedArgIdx::FUNC)] = TryGetFuncType(funcGT);
197 argTypes_[static_cast<size_t>(TypedArgIdx::NEW_TARGET)] = TryGetNewTargetType(tsManager, thisGT);
198 argTypes_[static_cast<size_t>(TypedArgIdx::THIS_OBJECT)] = TryGetThisType(tsManager, funcGT, thisGT);
199
200 if (funcGT.IsDefault()) {
201 return;
202 }
203 size_t extraParasNum = static_cast<size_t>(TypedArgIdx::NUM_OF_TYPED_ARGS);
204 uint32_t numExplicitArgs = tsManager->GetFunctionTypeLength(funcGT);
205 for (uint32_t explicitArgId = 0; explicitArgId < numExplicitArgs; explicitArgId++) {
206 argTypes_[extraParasNum++] = GateType(tsManager->GetFuncParameterTypeGT(funcGT, explicitArgId));
207 }
208 }
209
TryGetThisType(const TSManager * tsManager,GlobalTSTypeRef funcGT,GlobalTSTypeRef thisGT) const210 GateType TypeRecorder::TryGetThisType(const TSManager *tsManager, GlobalTSTypeRef funcGT, GlobalTSTypeRef thisGT) const
211 {
212 // The parameter 'this' may be declared explicitly, e.g. foo(this: Person, num: number). In this case, the type
213 // of 'this' is recorded in the type of the function. And this type is preferred over the type derived from
214 // 'thisGT' if both are given.
215 if (!funcGT.IsDefault()) {
216 auto gt = tsManager->GetFuncThisGT(funcGT);
217 if (!gt.IsDefault()) {
218 return GateType(gt);
219 }
220 }
221 return GateType(thisGT);
222 }
223
TryGetNewTargetType(const TSManager * tsManager,GlobalTSTypeRef thisGT) const224 GateType TypeRecorder::TryGetNewTargetType(const TSManager *tsManager, GlobalTSTypeRef thisGT) const
225 {
226 if (thisGT.IsDefault()) {
227 return GateType::AnyType();
228 }
229
230 GateType thisType(thisGT);
231 if (tsManager->IsClassInstanceTypeKind(thisType)) {
232 return GateType(tsManager->GetClassType(thisGT));
233 } else {
234 return thisType;
235 }
236 }
237
TryGetFuncType(GlobalTSTypeRef funcGT) const238 GateType TypeRecorder::TryGetFuncType(GlobalTSTypeRef funcGT) const
239 {
240 if (funcGT.IsDefault()) {
241 return GateType::AnyType();
242 }
243 return GateType(funcGT);
244 }
245
GetType(const int32_t offset) const246 GateType TypeRecorder::GetType(const int32_t offset) const
247 {
248 if (bcOffsetGtMap_.find(offset) != bcOffsetGtMap_.end()) {
249 return bcOffsetGtMap_.at(offset);
250 }
251 return GateType::AnyType();
252 }
253
GetArgType(const uint32_t argIndex) const254 GateType TypeRecorder::GetArgType(const uint32_t argIndex) const
255 {
256 ASSERT(argIndex < argTypes_.size());
257 return argTypes_[argIndex];
258 }
259
UpdateType(const int32_t offset,const GateType & type) const260 GateType TypeRecorder::UpdateType(const int32_t offset, const GateType &type) const
261 {
262 auto tempType = GetType(offset);
263 if (!tempType.IsAnyType()) {
264 ASSERT(type.IsAnyType());
265 return tempType;
266 }
267 return type;
268 }
269
GetOrUpdatePGOType(TSManager * tsManager,int32_t offset,const GateType & type) const270 PGOSampleType TypeRecorder::GetOrUpdatePGOType(TSManager *tsManager, int32_t offset, const GateType &type) const
271 {
272 if (bcOffsetPGOOpTypeMap_.find(offset) != bcOffsetPGOOpTypeMap_.end()) {
273 const auto iter = bcOffsetPGOOpTypeMap_.at(offset);
274 if (iter.IsClassType()) {
275 PGOHClassLayoutDesc *desc;
276 if (!decoder_->GetHClassLayoutDesc(iter, &desc)) {
277 return PGOSampleType::NoneClassType();
278 }
279 TSHClassGenerator generator(tsManager);
280 generator.UpdateTSHClassFromPGO(type, *desc, enableOptTrackField_);
281 }
282 return iter;
283 }
284
285 return PGOSampleType::NoneType();
286 }
287
GetCallTargetType(int32_t offset) const288 GateType TypeRecorder::GetCallTargetType(int32_t offset) const
289 {
290 if (bcOffsetCallTargetGtMap_.find(offset) != bcOffsetCallTargetGtMap_.end()) {
291 return bcOffsetCallTargetGtMap_.at(offset);
292 }
293 return GateType::AnyType();
294 }
295
GetRwOpType(int32_t offset) const296 PGORWOpType TypeRecorder::GetRwOpType(int32_t offset) const
297 {
298 if (bcOffsetPGORwTypeMap_.find(offset) != bcOffsetPGORwTypeMap_.end()) {
299 return bcOffsetPGORwTypeMap_.at(offset);
300 }
301 return PGORWOpType();
302 }
303
GetElementsKind(int32_t offset) const304 ElementsKind TypeRecorder::GetElementsKind(int32_t offset) const
305 {
306 if (bcOffsetPGORwTypeMap_.find(offset) == bcOffsetPGORwTypeMap_.end()) {
307 return ElementsKind::GENERIC;
308 }
309
310 PGORWOpType rwType = bcOffsetPGORwTypeMap_.at(offset);
311 PGOObjectInfo info = rwType.GetObjectInfo(0);
312 if (info.IsNone()) {
313 return ElementsKind::GENERIC;
314 }
315
316 PGOSampleType type(info.GetClassType());
317 PGOHClassLayoutDesc *desc;
318 if (!decoder_->GetHClassLayoutDesc(type, &desc)) {
319 return ElementsKind::GENERIC;
320 }
321
322 auto elementsKind = desc->GetElementsKind();
323 return elementsKind;
324 }
325
TypeNeedFilter(GlobalTSTypeRef gt) const326 bool TypeRecorder::TypeNeedFilter(GlobalTSTypeRef gt) const
327 {
328 return gt.IsDefault() || gt.IsGenericsModule();
329 }
330 } // namespace panda::ecmascript
331