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