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