• 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/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