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 #include "ecmascript/compiler/bytecode_circuit_builder.h"
16 #include "ecmascript/compiler/bytecodes.h"
17 #include "ecmascript/compiler/compiler_log.h"
18 #include "ecmascript/compiler/pass.h"
19 #include "ecmascript/compiler/ts_inline_lowering.h"
20 #include "ecmascript/ts_types/ts_manager.h"
21 #include "ecmascript/ts_types/ts_type.h"
22 #include "libpandabase/utils/utf.h"
23 #include "libpandafile/class_data_accessor-inl.h"
24 #include "ecmascript/ts_types/ts_type_accessor.h"
25
26 namespace panda::ecmascript::kungfu {
RunTSInlineLowering()27 void TSInlineLowering::RunTSInlineLowering()
28 {
29 circuit_->AdvanceTime();
30 ChunkQueue<CallGateInfo> workList(chunk_);
31 UpdateWorkList(workList);
32
33 while (!workList.empty()) {
34 CallGateInfo info = workList.front();
35 workList.pop();
36 TryInline(info, workList);
37 }
38 }
39
CandidateInlineCall(GateRef gate,ChunkQueue<CallGateInfo> & workList)40 void TSInlineLowering::CandidateInlineCall(GateRef gate, ChunkQueue<CallGateInfo> &workList)
41 {
42 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
43 switch (ecmaOpcode) {
44 case EcmaOpcode::LDOBJBYNAME_IMM8_ID16:
45 case EcmaOpcode::LDOBJBYNAME_IMM16_ID16:
46 case EcmaOpcode::LDTHISBYNAME_IMM8_ID16:
47 case EcmaOpcode::LDTHISBYNAME_IMM16_ID16:
48 CandidateAccessor(gate, workList, CallKind::CALL_GETTER);
49 break;
50 case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8:
51 case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8:
52 case EcmaOpcode::STTHISBYNAME_IMM8_ID16:
53 case EcmaOpcode::STTHISBYNAME_IMM16_ID16:
54 CandidateAccessor(gate, workList, CallKind::CALL_SETTER);
55 break;
56 case EcmaOpcode::CALLTHIS0_IMM8_V8:
57 case EcmaOpcode::CALLTHIS1_IMM8_V8_V8:
58 case EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8:
59 case EcmaOpcode::CALLTHIS3_IMM8_V8_V8_V8_V8:
60 case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8:
61 case EcmaOpcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8:
62 CandidateNormalCall(gate, workList, CallKind::CALL_THIS);
63 break;
64 case EcmaOpcode::CALLARG0_IMM8:
65 case EcmaOpcode::CALLARG1_IMM8_V8:
66 case EcmaOpcode::CALLARGS2_IMM8_V8_V8:
67 case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8:
68 case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8:
69 case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8:
70 CandidateNormalCall(gate, workList, CallKind::CALL);
71 break;
72 default:
73 break;
74 }
75 }
76
TryInline(CallGateInfo & info,ChunkQueue<CallGateInfo> & workList)77 void TSInlineLowering::TryInline(CallGateInfo &info, ChunkQueue<CallGateInfo> &workList)
78 {
79 GateRef gate = info.GetCallGate();
80 // inline doesn't support try-catch
81 bool inTryCatch = FilterCallInTryCatch(gate);
82 if (inTryCatch) {
83 return;
84 }
85
86 MethodLiteral* inlinedMethod = nullptr;
87 GlobalTSTypeRef gt = info.GetFuncGT();
88 auto methodOffset = tsManager_->GetFuncMethodOffset(gt);
89 if (methodOffset == 0 || ctx_->IsSkippedMethod(methodOffset)) {
90 return;
91 }
92 if (IsRecursiveFunc(info, methodOffset)) {
93 return;
94 }
95 inlinedMethod = ctx_->GetJSPandaFile()->FindMethodLiteral(methodOffset);
96 if (!CheckParameter(gate, info, inlinedMethod)) {
97 return;
98 }
99 auto &bytecodeInfo = ctx_->GetBytecodeInfo();
100 auto &methodInfo = bytecodeInfo.GetMethodList().at(methodOffset);
101 auto &methodPcInfos = bytecodeInfo.GetMethodPcInfos();
102 auto &methodPcInfo = methodPcInfos[methodInfo.GetMethodPcInfoIndex()];
103 GateRef frameState = GetFrameState(info);
104 GateRef frameArgs = acc_.GetValueIn(frameState);
105 size_t inlineCallCounts = GetOrInitialInlineCounts(frameArgs);
106 if (IsSmallMethod(methodPcInfo.pcOffsets.size()) && !IsInlineCountsOverflow(inlineCallCounts)) {
107 inlineSuccess_ = FilterInlinedMethod(inlinedMethod, methodPcInfo.pcOffsets);
108 if (inlineSuccess_) {
109 GateRef glue = acc_.GetGlueFromArgList();
110 SetInitCallTargetAndConstPoolId(info);
111 CircuitRootScope scope(circuit_);
112 AnalyseFastAccessor(info, methodPcInfo.pcOffsets, methodOffset);
113 if (!noCheck_) {
114 InlineCheck(info);
115 }
116 InlineCall(methodInfo, methodPcInfo, inlinedMethod, info);
117 ReplaceInput(info, glue, inlinedMethod);
118 UpdateInlineCounts(frameArgs, inlineCallCounts);
119 if (info.IsNormalCall()) {
120 UpdateWorkList(workList);
121 } else {
122 lastCallId_ = circuit_->GetGateCount() - 1;
123 }
124 }
125 }
126
127 if ((inlinedMethod != nullptr) && IsLogEnabled() && inlineSuccess_) {
128 inlineSuccess_ = false;
129 auto jsPandaFile = ctx_->GetJSPandaFile();
130 const std::string methodName(
131 MethodLiteral::GetMethodName(jsPandaFile, inlinedMethod->GetMethodId()));
132 std::string fileName = jsPandaFile->GetFileName();
133 const std::string recordName(MethodLiteral::GetRecordName(jsPandaFile, inlinedMethod->GetMethodId()));
134 std::string fullName = methodName + "@" + recordName + "@" + fileName;
135 LOG_COMPILER(INFO) << "";
136 LOG_COMPILER(INFO) << "\033[34m"
137 << "===================="
138 << " After inlining "
139 << "[" << fullName << "]"
140 << " Caller method "
141 << "[" << methodName_ << "]"
142 << "===================="
143 << "\033[0m";
144 circuit_->PrintAllGatesWithBytecode();
145 LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m";
146 }
147 }
148
FilterInlinedMethod(MethodLiteral * method,std::vector<const uint8_t * > pcOffsets)149 bool TSInlineLowering::FilterInlinedMethod(MethodLiteral* method, std::vector<const uint8_t*> pcOffsets)
150 {
151 const JSPandaFile *jsPandaFile = ctx_->GetJSPandaFile();
152 const panda_file::File *pf = jsPandaFile->GetPandaFile();
153 panda_file::MethodDataAccessor mda(*pf, method->GetMethodId());
154 panda_file::CodeDataAccessor cda(*pf, mda.GetCodeId().value());
155 if (cda.GetTriesSize() != 0) {
156 return false;
157 }
158 for (size_t i = 0; i < pcOffsets.size(); i++) {
159 auto pc = pcOffsets[i];
160 auto ecmaOpcode = ctx_->GetByteCodes()->GetOpcode(pc);
161 switch (ecmaOpcode) {
162 case EcmaOpcode::GETUNMAPPEDARGS:
163 case EcmaOpcode::SUSPENDGENERATOR_V8:
164 case EcmaOpcode::RESUMEGENERATOR:
165 case EcmaOpcode::COPYRESTARGS_IMM8:
166 case EcmaOpcode::WIDE_COPYRESTARGS_PREF_IMM16:
167 case EcmaOpcode::CREATEASYNCGENERATOROBJ_V8:
168 return false;
169 default:
170 break;
171 }
172 }
173 return true;
174 }
175
InlineCall(MethodInfo & methodInfo,MethodPcInfo & methodPCInfo,MethodLiteral * method,CallGateInfo & info)176 void TSInlineLowering::InlineCall(MethodInfo &methodInfo, MethodPcInfo &methodPCInfo, MethodLiteral* method,
177 CallGateInfo &info)
178 {
179 const JSPandaFile *jsPandaFile = ctx_->GetJSPandaFile();
180 TSManager *tsManager = ctx_->GetTSManager();
181 CompilerLog *log = ctx_->GetCompilerLog();
182 CString recordName = MethodLiteral::GetRecordName(jsPandaFile, method->GetMethodId());
183 bool hasTyps = jsPandaFile->HasTSTypes(recordName);
184 if (!hasTyps) {
185 return;
186 }
187 const std::string methodName(MethodLiteral::GetMethodName(jsPandaFile, method->GetMethodId()));
188 std::string fileName = jsPandaFile->GetFileName();
189 std::string fullName = methodName + "@" + std::string(recordName) + "@" + fileName;
190
191 circuit_->InitRoot();
192 BytecodeCircuitBuilder builder(jsPandaFile, method, methodPCInfo,
193 tsManager, circuit_,
194 ctx_->GetByteCodes(), true, IsLogEnabled(),
195 enableTypeLowering_, fullName, recordName, ctx_->GetPfDecoder(), true,
196 passOptions_->EnableOptTrackField());
197 {
198 if (enableTypeLowering_) {
199 BuildFrameStateChain(info, builder);
200 }
201 TimeScope timeScope("BytecodeToCircuit", methodName, method->GetMethodId().GetOffset(), log);
202 builder.BytecodeToCircuit();
203 }
204
205 PassData data(&builder, circuit_, ctx_, log, fullName,
206 &methodInfo, hasTyps, recordName,
207 method, method->GetMethodId().GetOffset(), nativeAreaAllocator_, ctx_->GetPfDecoder(), passOptions_);
208 PassRunner<PassData> pipeline(&data);
209 if (builder.EnableLoopOptimization()) {
210 pipeline.RunPass<LoopOptimizationPass>();
211 }
212 pipeline.RunPass<TypeInferPass>();
213 pipeline.RunPass<PGOTypeInferPass>();
214 }
215
CheckParameter(GateRef gate,CallGateInfo & info,MethodLiteral * method)216 bool TSInlineLowering::CheckParameter(GateRef gate, CallGateInfo &info, MethodLiteral* method)
217 {
218 if (info.IsCallAccessor()) {
219 return true;
220 }
221 size_t numIns = acc_.GetNumValueIn(gate);
222 size_t fixedInputsNum = info.IsCallThis() ? 2 : 1; // 2: calltarget and this
223
224 uint32_t declaredNumArgs = method->GetNumArgsWithCallField();
225 return declaredNumArgs == (numIns - fixedInputsNum);
226 }
227
ReplaceCallInput(CallGateInfo & info,GateRef glue,MethodLiteral * method)228 void TSInlineLowering::ReplaceCallInput(CallGateInfo &info, GateRef glue, MethodLiteral *method)
229 {
230 GateRef gate = info.GetCallGate();
231 bool isCallThis = info.IsCallThis();
232 std::vector<GateRef> vec;
233 size_t numIns = acc_.GetNumValueIn(gate);
234 // 1: last one elem is function
235 GateRef callTarget = acc_.GetValueIn(gate, numIns - 1);
236 GateRef thisObj = Circuit::NullGate();
237 size_t fixedInputsNum = 0;
238 if (isCallThis) {
239 fixedInputsNum = 2; // 2: call target and this
240 thisObj = acc_.GetValueIn(gate, 0);
241 } else {
242 fixedInputsNum = 1; // 1: call target
243 thisObj = builder_.Undefined();
244 }
245 // -1: callTarget
246 size_t actualArgc = numIns + NUM_MANDATORY_JSFUNC_ARGS - fixedInputsNum;
247 vec.emplace_back(glue); // glue
248 if (!method->IsFastCall()) {
249 vec.emplace_back(builder_.Int64(actualArgc)); // argc
250 }
251 vec.emplace_back(callTarget);
252 if (!method->IsFastCall()) {
253 vec.emplace_back(builder_.Undefined()); // newTarget
254 }
255 vec.emplace_back(thisObj);
256 // -1: call Target
257 for (size_t i = fixedInputsNum - 1; i < numIns - 1; i++) {
258 vec.emplace_back(acc_.GetValueIn(gate, i));
259 }
260 LowerToInlineCall(info, vec, method);
261 }
262
ReplaceAccessorInput(CallGateInfo & info,GateRef glue,MethodLiteral * method)263 void TSInlineLowering::ReplaceAccessorInput(CallGateInfo &info, GateRef glue, MethodLiteral *method)
264 {
265 GateRef gate = info.GetCallGate();
266 std::vector<GateRef> vec;
267 GateRef thisObj = GetAccessorReceiver(gate);
268 GateRef callTarget = Circuit::NullGate();
269 // Fast accessor will not load getter or setter func
270 if (EnableFastAccessor()) {
271 callTarget = initCallTarget_;
272 } else {
273 callTarget = BuildAccessor(info);
274 }
275 size_t actualArgc = 0;
276 if (info.IsCallGetter()) {
277 actualArgc = NUM_MANDATORY_JSFUNC_ARGS;
278 } else if (info.IsCallSetter()) {
279 actualArgc = NUM_MANDATORY_JSFUNC_ARGS + 1;
280 } else {
281 UNREACHABLE();
282 }
283
284 vec.emplace_back(glue); // glue
285 if (!method->IsFastCall()) {
286 vec.emplace_back(builder_.Int64(actualArgc)); // argc
287 }
288 vec.emplace_back(callTarget);
289 if (!method->IsFastCall()) {
290 vec.emplace_back(builder_.Undefined()); // newTarget
291 }
292 vec.emplace_back(thisObj);
293
294 if (info.IsCallSetter()) {
295 vec.emplace_back(GetCallSetterValue(gate));
296 }
297 LowerToInlineCall(info, vec, method);
298 }
299
BuildAccessor(CallGateInfo & info)300 GateRef TSInlineLowering::BuildAccessor(CallGateInfo &info)
301 {
302 GateRef gate = info.GetCallGate();
303 GateRef depend = acc_.GetDep(gate);
304 GateRef receiver = GetAccessorReceiver(gate);
305 GateRef accessor = Circuit::NullGate();
306 uint32_t plrData = GetPlrData(receiver, acc_.GetValueIn(gate, 1));
307 if (info.IsCallGetter()) {
308 accessor = circuit_->NewGate(circuit_->LoadGetter(), MachineType::I64,
309 {depend, receiver, builder_.Int32(plrData)}, GateType::AnyType());
310 } else {
311 accessor = circuit_->NewGate(circuit_->LoadSetter(), MachineType::I64,
312 {depend, receiver, builder_.Int32(plrData)}, GateType::AnyType());
313 }
314 acc_.ReplaceDependIn(gate, accessor);
315 return accessor;
316 }
317
GetPlrData(GateRef receiver,GateRef constData)318 uint32_t TSInlineLowering::GetPlrData(GateRef receiver, GateRef constData)
319 {
320 uint16_t propIndex = acc_.GetConstantValue(constData);
321 auto prop = tsManager_->GetStringFromConstantPool(propIndex);
322 GateType receiverType = acc_.GetGateType(receiver);
323 receiverType = tsManager_->TryNarrowUnionType(receiverType);
324 int hclassIndex = tsManager_->GetHClassIndexByInstanceGateType(receiverType);
325 JSHClass *hclass = JSHClass::Cast(tsManager_->GetValueFromCache(hclassIndex).GetTaggedObject());
326 PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(tsManager_->GetThread(), hclass, prop);
327 return plr.GetData();
328 }
329
ReplaceInput(CallGateInfo & info,GateRef glue,MethodLiteral * method)330 void TSInlineLowering::ReplaceInput(CallGateInfo &info, GateRef glue, MethodLiteral *method)
331 {
332 if (info.IsNormalCall()) {
333 ReplaceCallInput(info, glue, method);
334 } else {
335 ASSERT(info.IsCallAccessor());
336 ReplaceAccessorInput(info, glue, method);
337 }
338 }
339
MergeAllReturn(const std::vector<GateRef> & returnVector,GateRef & state,GateRef & depend)340 GateRef TSInlineLowering::MergeAllReturn(const std::vector<GateRef> &returnVector, GateRef &state, GateRef &depend)
341 {
342 size_t numOfIns = returnVector.size();
343 auto stateList = std::vector<GateRef>(numOfIns, Circuit::NullGate());
344 auto dependList = std::vector<GateRef>(numOfIns + 1, Circuit::NullGate());
345 auto vaueList = std::vector<GateRef>(numOfIns + 1, Circuit::NullGate());
346
347 for (size_t i = 0; i < returnVector.size(); i++) {
348 GateRef returnGate = returnVector.at(i);
349 ASSERT(acc_.GetOpCode(acc_.GetState(returnGate)) != OpCode::IF_EXCEPTION);
350 stateList[i] = acc_.GetState(returnGate);
351 dependList[i + 1] = acc_.GetDep(returnGate);
352 vaueList[i + 1] = acc_.GetValueIn(returnGate, 0);
353 acc_.DeleteGate(returnGate);
354 }
355
356 state = circuit_->NewGate(circuit_->Merge(numOfIns), stateList);
357 dependList[0] = state;
358 vaueList[0] = state;
359 depend = circuit_->NewGate(circuit_->DependSelector(numOfIns), dependList);
360 return circuit_->NewGate(circuit_->ValueSelector(numOfIns), MachineType::I64, numOfIns + 1,
361 vaueList.data(), GateType::AnyType());
362 }
363
ReplaceEntryGate(GateRef callGate,GateRef callerFunc,GateRef inlineFunc,GateRef glue)364 void TSInlineLowering::ReplaceEntryGate(GateRef callGate, GateRef callerFunc, GateRef inlineFunc, GateRef glue)
365 {
366 auto stateEntry = acc_.GetStateRoot();
367 auto dependEntry = acc_.GetDependRoot();
368
369 GateRef callState = acc_.GetState(callGate);
370 GateRef callDepend = acc_.GetDep(callGate);
371 auto stateUse = acc_.Uses(stateEntry);
372
373 // support inline trace
374 GateRef newDep = Circuit::NullGate();
375 if (traceInline_) {
376 std::vector<GateRef> args{callerFunc, inlineFunc};
377 newDep = TraceInlineFunction(glue, callDepend, args, callGate);
378 } else {
379 newDep = callDepend;
380 }
381
382 for (auto stateUseIt = stateUse.begin(); stateUseIt != stateUse.end();) {
383 stateUseIt = acc_.ReplaceIn(stateUseIt, callState);
384 }
385
386 auto dependUse = acc_.Uses(dependEntry);
387 for (auto dependUseIt = dependUse.begin(); dependUseIt != dependUse.end();) {
388 dependUseIt = acc_.ReplaceIn(dependUseIt, newDep);
389 }
390 }
391
TraceInlineFunction(GateRef glue,GateRef depend,std::vector<GateRef> & args,GateRef callGate)392 GateRef TSInlineLowering::TraceInlineFunction(GateRef glue, GateRef depend, std::vector<GateRef> &args,
393 GateRef callGate)
394 {
395 size_t index = RTSTUB_ID(AotInlineTrace);
396 GateRef result = builder_.NoLabelCallRuntime(glue, depend, index, args, callGate);
397 return result;
398 }
399
ReplaceReturnGate(GateRef callGate)400 void TSInlineLowering::ReplaceReturnGate(GateRef callGate)
401 {
402 std::vector<GateRef> returnVector;
403 acc_.GetReturnOuts(returnVector);
404
405 GateRef value = Circuit::NullGate();
406 GateRef state = Circuit::NullGate();
407 GateRef depend = Circuit::NullGate();
408
409 if (returnVector.size() == 1) {
410 GateRef returnGate = returnVector.at(0);
411 GateRef returnState = acc_.GetState(returnGate);
412 depend = acc_.GetDep(returnGate);
413 state = returnState;
414 value = acc_.GetValueIn(returnGate, 0);
415 acc_.DeleteGate(returnGate);
416 } else {
417 value = MergeAllReturn(returnVector, state, depend);
418 }
419 SupplementType(callGate, value);
420 ReplaceHirAndDeleteState(callGate, state, depend, value);
421 }
422
ReplaceHirAndDeleteState(GateRef gate,GateRef state,GateRef depend,GateRef value)423 void TSInlineLowering::ReplaceHirAndDeleteState(GateRef gate, GateRef state, GateRef depend, GateRef value)
424 {
425 auto uses = acc_.Uses(gate);
426 for (auto useIt = uses.begin(); useIt != uses.end();) {
427 if (acc_.IsStateIn(useIt)) {
428 useIt = acc_.ReplaceIn(useIt, state);
429 } else if (acc_.IsDependIn(useIt)) {
430 useIt = acc_.ReplaceIn(useIt, depend);
431 } else if (acc_.IsValueIn(useIt)) {
432 useIt = acc_.ReplaceIn(useIt, value);
433 } else {
434 LOG_ECMA(FATAL) << "this branch is unreachable";
435 UNREACHABLE();
436 }
437 }
438 acc_.DeleteGate(gate);
439 }
440
LowerToInlineCall(CallGateInfo & info,const std::vector<GateRef> & args,MethodLiteral * method)441 void TSInlineLowering::LowerToInlineCall(CallGateInfo &info, const std::vector<GateRef> &args, MethodLiteral* method)
442 {
443 GateRef callGate = info.GetCallGate();
444 // replace in value/args
445 ArgumentAccessor argAcc(circuit_);
446 ASSERT(argAcc.ArgsCount() == args.size());
447 for (size_t i = 0; i < argAcc.ArgsCount(); i++) {
448 GateRef arg = argAcc.ArgsAt(i);
449 acc_.UpdateAllUses(arg, args.at(i));
450 acc_.SetGateType(args.at(i), acc_.GetGateType(arg));
451 acc_.DeleteGate(arg);
452 }
453 // replace in depend and state
454 GateRef glue = args.at(static_cast<size_t>(CommonArgIdx::GLUE));
455 GateRef inlineFunc;
456 if (method->IsFastCall()) {
457 inlineFunc = args.at(static_cast<size_t>(FastCallArgIdx::FUNC));
458 } else {
459 inlineFunc = args.at(static_cast<size_t>(CommonArgIdx::FUNC));
460 }
461 GateRef frameArgs = GetFrameArgs(info);
462 GateRef callerFunc = acc_.GetValueIn(frameArgs, 0);
463 ReplaceEntryGate(callGate, callerFunc, inlineFunc, glue);
464 // replace use gate
465 ReplaceReturnGate(callGate);
466 // remove Useless root gates
467 RemoveRoot();
468 }
469
InlineFuncCheck(const CallGateInfo & info)470 void TSInlineLowering::InlineFuncCheck(const CallGateInfo &info)
471 {
472 GateRef gate = info.GetCallGate();
473 GateRef callState = acc_.GetState(gate);
474 GateRef callDepend = acc_.GetDep(gate);
475 ASSERT(acc_.HasFrameState(gate));
476 GateRef frameState = acc_.GetFrameState(gate);
477 size_t funcIndex = acc_.GetNumValueIn(gate) - 1;
478 GateRef inlineFunc = acc_.GetValueIn(gate, funcIndex);
479 // Do not load from inlineFunc beacause type in inlineFunc could be modified by others
480 GlobalTSTypeRef funcGt = info.GetFuncGT();
481 auto methodOffset = tsManager_->GetFuncMethodOffset(funcGt);
482 GateRef ret = circuit_->NewGate(circuit_->JSInlineTargetTypeCheck(info.GetType()),
483 MachineType::I1, {callState, callDepend, inlineFunc, builder_.IntPtr(methodOffset), frameState},
484 GateType::NJSValue());
485 acc_.ReplaceStateIn(gate, ret);
486 acc_.ReplaceDependIn(gate, ret);
487 }
488
InlineAccessorCheck(GateRef gate,GateRef receiver)489 void TSInlineLowering::InlineAccessorCheck(GateRef gate, GateRef receiver)
490 {
491 GateRef callState = acc_.GetState(gate);
492 GateRef callDepend = acc_.GetDep(gate);
493 GateType receiverType = acc_.GetGateType(receiver);
494 receiverType = tsManager_->TryNarrowUnionType(receiverType);
495 int hclassIndex = tsManager_->GetHClassIndexByInstanceGateType(receiverType);
496 GateRef hclassIndexGate = builder_.IntPtr(hclassIndex);
497 auto frameState = acc_.FindNearestFrameState(callDepend);
498 GateRef ret = circuit_->NewGate(circuit_->InlineAccessorCheck(static_cast<size_t>(receiverType.Value())),
499 MachineType::I1, {callState, callDepend, receiver, hclassIndexGate, frameState}, GateType::NJSValue());
500 acc_.ReplaceStateIn(gate, ret);
501 acc_.ReplaceDependIn(gate, ret);
502 }
503
InlineCheck(CallGateInfo & info)504 void TSInlineLowering::InlineCheck(CallGateInfo &info)
505 {
506 if (info.IsNormalCall()) {
507 InlineFuncCheck(info);
508 } else {
509 ASSERT(info.IsCallAccessor());
510 GateRef gate = info.GetCallGate();
511 GateRef receiver = GetAccessorReceiver(gate);
512 InlineAccessorCheck(gate, receiver);
513 }
514 }
515
RemoveRoot()516 void TSInlineLowering::RemoveRoot()
517 {
518 GateRef circuitRoot = acc_.GetCircuitRoot();
519 auto uses = acc_.Uses(circuitRoot);
520 for (auto it = uses.begin(); it != uses.end();) {
521 it = acc_.DeleteGate(it);
522 }
523
524 acc_.DeleteGate(circuitRoot);
525 }
526
BuildFrameStateChain(CallGateInfo & info,BytecodeCircuitBuilder & builder)527 void TSInlineLowering::BuildFrameStateChain(CallGateInfo &info, BytecodeCircuitBuilder &builder)
528 {
529 GateRef preFrameState = GetFrameState(info);
530 ASSERT(acc_.GetOpCode(preFrameState) == OpCode::FRAME_STATE);
531 builder.SetPreFrameState(preFrameState);
532 }
533
FilterCallInTryCatch(GateRef gate)534 bool TSInlineLowering::FilterCallInTryCatch(GateRef gate)
535 {
536 auto uses = acc_.Uses(gate);
537 for (auto it = uses.begin(); it != uses.end(); ++it) {
538 if (acc_.GetOpCode(*it) == OpCode::IF_SUCCESS || acc_.GetOpCode(*it) == OpCode::IF_EXCEPTION) {
539 return true;
540 }
541 }
542 return false;
543 }
544
SupplementType(GateRef callGate,GateRef targetGate)545 void TSInlineLowering::SupplementType(GateRef callGate, GateRef targetGate)
546 {
547 GateType callGateType = acc_.GetGateType(callGate);
548 GateType targetGateType = acc_.GetGateType(targetGate);
549 if (!callGateType.IsAnyType() && targetGateType.IsAnyType()) {
550 acc_.SetGateType(targetGate, callGateType);
551 }
552 }
553
UpdateWorkList(ChunkQueue<CallGateInfo> & workList)554 void TSInlineLowering::UpdateWorkList(ChunkQueue<CallGateInfo> &workList)
555 {
556 std::vector<GateRef> gateList;
557 circuit_->GetAllGates(gateList);
558 for (const auto &gate : gateList) {
559 if (acc_.GetId(gate) <= lastCallId_) {
560 continue;
561 }
562 auto op = acc_.GetOpCode(gate);
563 if (op == OpCode::JS_BYTECODE) {
564 CandidateInlineCall(gate, workList);
565 }
566 }
567 }
568
GetOrInitialInlineCounts(GateRef frameArgs)569 size_t TSInlineLowering::GetOrInitialInlineCounts(GateRef frameArgs)
570 {
571 auto it = inlinedCallMap_.find(frameArgs);
572 if (it == inlinedCallMap_.end()) {
573 inlinedCallMap_[frameArgs] = 0;
574 }
575 return inlinedCallMap_[frameArgs];
576 }
577
578
IsRecursiveFunc(CallGateInfo & info,size_t calleeMethodOffset)579 bool TSInlineLowering::IsRecursiveFunc(CallGateInfo &info, size_t calleeMethodOffset)
580 {
581 GateRef frameArgs = GetFrameArgs(info);
582 GateRef caller = acc_.GetValueIn(frameArgs);
583 auto funcType = acc_.GetGateType(caller);
584 GlobalTSTypeRef gt = funcType.GetGTRef();
585 if (!tsManager_->IsFunctionTypeKind(gt)) {
586 return false;
587 }
588 auto callerMethodOffset = tsManager_->GetFuncMethodOffset(gt);
589 return callerMethodOffset == calleeMethodOffset;
590 }
591
IsAccessor(GateRef receiver,GateRef constData)592 bool TSInlineLowering::IsAccessor(GateRef receiver, GateRef constData)
593 {
594 uint16_t propIndex = acc_.GetConstantValue(constData);
595 auto prop = tsManager_->GetStringFromConstantPool(propIndex);
596 GateType receiverType = acc_.GetGateType(receiver);
597 receiverType = tsManager_->TryNarrowUnionType(receiverType);
598 if (tsManager_->IsClassInstanceTypeKind(receiverType)) {
599 int hclassIndex = tsManager_->GetHClassIndexByInstanceGateType(receiverType);
600 if (hclassIndex == -1) {
601 return false;
602 }
603 JSHClass *hclass = JSHClass::Cast(tsManager_->GetValueFromCache(hclassIndex).GetTaggedObject());
604 if (!hclass->HasTSSubtyping()) {
605 return false;
606 }
607 PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(tsManager_->GetThread(), hclass, prop);
608 if (!plr.IsFound()) {
609 return false;
610 }
611
612 return plr.IsAccessor();
613 }
614 return false;
615 }
616
GetAccessorFuncGT(GateRef receiver,GateRef constData)617 GlobalTSTypeRef TSInlineLowering::GetAccessorFuncGT(GateRef receiver, GateRef constData)
618 {
619 GateType receiverType = acc_.GetGateType(receiver);
620 receiverType = tsManager_->TryNarrowUnionType(receiverType);
621 GlobalTSTypeRef classInstanceGT = receiverType.GetGTRef();
622 GlobalTSTypeRef classGT = tsManager_->GetClassType(classInstanceGT);
623 TSTypeAccessor tsTypeAcc(tsManager_, classGT);
624 uint16_t propIndex = acc_.GetConstantValue(constData);
625 auto prop = tsManager_->GetStringFromConstantPool(propIndex);
626 GlobalTSTypeRef funcGT = tsTypeAcc.GetPrototypePropGT(prop);
627 return funcGT;
628 }
629
CandidateAccessor(GateRef gate,ChunkQueue<CallGateInfo> & workList,CallKind kind)630 void TSInlineLowering::CandidateAccessor(GateRef gate, ChunkQueue<CallGateInfo> &workList, CallKind kind)
631 {
632 GateRef receiver = GetAccessorReceiver(gate);
633 GateRef constData = acc_.GetValueIn(gate, 1);
634 if (IsAccessor(receiver, constData)) {
635 GlobalTSTypeRef gt = GetAccessorFuncGT(receiver, constData);
636 if (!gt.IsDefault()) {
637 workList.push(CallGateInfo(gate, kind, gt, 0));
638 lastCallId_ = acc_.GetId(gate);
639 }
640 }
641 }
642
CandidateNormalCall(GateRef gate,ChunkQueue<CallGateInfo> & workList,CallKind kind)643 void TSInlineLowering::CandidateNormalCall(GateRef gate, ChunkQueue<CallGateInfo> &workList, CallKind kind)
644 {
645 size_t funcIndex = acc_.GetNumValueIn(gate) - 1;
646 auto funcType = acc_.GetGateType(acc_.GetValueIn(gate, funcIndex));
647 if (tsManager_->IsFunctionTypeKind(funcType)) {
648 GlobalTSTypeRef gt = funcType.GetGTRef();
649 workList.push(CallGateInfo(gate, kind, gt, funcType.Value()));
650 lastCallId_ = acc_.GetId(gate);
651 }
652 }
653
GetAccessorReceiver(GateRef gate)654 GateRef TSInlineLowering::GetAccessorReceiver(GateRef gate)
655 {
656 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
657 if (UNLIKELY(ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM8_ID16 ||
658 ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM16_ID16)) {
659 return argAcc_.GetFrameArgsIn(gate, FrameArgIdx::THIS_OBJECT);
660 }
661 return acc_.GetValueIn(gate, 2); // 2: receiver
662 }
663
GetCallSetterValue(GateRef gate)664 GateRef TSInlineLowering::GetCallSetterValue(GateRef gate)
665 {
666 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
667 if (ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM8_ID16 ||
668 ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM16_ID16) {
669 return acc_.GetValueIn(gate, 2); // 2: value
670 }
671 return acc_.GetValueIn(gate, 3); // 3: value
672 }
673
GetFrameState(CallGateInfo & info)674 GateRef TSInlineLowering::GetFrameState(CallGateInfo &info)
675 {
676 GateRef gate = info.GetCallGate();
677 if (info.IsNormalCall()) {
678 return acc_.GetFrameState(gate);
679 }
680 ASSERT(info.IsCallAccessor());
681 GateRef frameState = acc_.FindNearestFrameState(gate);
682 return frameState;
683 }
684
GetFrameArgs(CallGateInfo & info)685 GateRef TSInlineLowering::GetFrameArgs(CallGateInfo &info)
686 {
687 GateRef frameState = GetFrameState(info);
688 return acc_.GetValueIn(frameState);
689 }
690
SetInitCallTargetAndConstPoolId(CallGateInfo & info)691 void TSInlineLowering::SetInitCallTargetAndConstPoolId(CallGateInfo &info)
692 {
693 if (initCallTarget_ == Circuit::NullGate()) {
694 GateRef frameArgs = GetFrameArgs(info);
695 initCallTarget_ = acc_.GetValueIn(frameArgs, 0);
696 const JSPandaFile *pf = ctx_->GetJSPandaFile();
697 initConstantPoolId_ = tsManager_->GetConstantPoolIDByMethodOffset(pf, initMethodOffset_);
698 }
699 }
700
AnalyseFastAccessor(CallGateInfo & info,std::vector<const uint8_t * > pcOffsets,uint32_t inlineMethodOffset)701 void TSInlineLowering::AnalyseFastAccessor(CallGateInfo &info, std::vector<const uint8_t*> pcOffsets,
702 uint32_t inlineMethodOffset)
703 {
704 isFastAccessor_ = false;
705 if (!info.IsCallAccessor()) {
706 return;
707 }
708 const JSPandaFile *pf = ctx_->GetJSPandaFile();
709 int32_t constantpoolId = tsManager_->GetConstantPoolIDByMethodOffset(pf, inlineMethodOffset);
710 if (constantpoolId == initConstantPoolId_) {
711 for (size_t i = 0; i < pcOffsets.size(); i++) {
712 auto pc = pcOffsets[i];
713 auto ecmaOpcode = ctx_->GetByteCodes()->GetOpcode(pc);
714 // These bytecodes require calltarget during the lowering process, so the acquisition of the accessor
715 // function cannot be omitted.
716 switch (ecmaOpcode) {
717 case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16:
718 case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16:
719 case EcmaOpcode::STMODULEVAR_IMM8:
720 case EcmaOpcode::WIDE_STMODULEVAR_PREF_IMM16:
721 case EcmaOpcode::DYNAMICIMPORT:
722 case EcmaOpcode::LDLOCALMODULEVAR_IMM8:
723 case EcmaOpcode::WIDE_LDLOCALMODULEVAR_PREF_IMM16:
724 case EcmaOpcode::LDEXTERNALMODULEVAR_IMM8:
725 case EcmaOpcode::WIDE_LDEXTERNALMODULEVAR_PREF_IMM16:
726 case EcmaOpcode::GETMODULENAMESPACE_IMM8:
727 case EcmaOpcode::WIDE_GETMODULENAMESPACE_PREF_IMM16:
728 case EcmaOpcode::SUPERCALLSPREAD_IMM8_V8:
729 case EcmaOpcode::NEWLEXENVWITHNAME_IMM8_ID16:
730 case EcmaOpcode::WIDE_NEWLEXENVWITHNAME_PREF_IMM16_ID16:
731 case EcmaOpcode::LDSUPERBYVALUE_IMM8_V8:
732 case EcmaOpcode::LDSUPERBYVALUE_IMM16_V8:
733 case EcmaOpcode::STSUPERBYVALUE_IMM16_V8_V8:
734 case EcmaOpcode::STSUPERBYVALUE_IMM8_V8_V8:
735 case EcmaOpcode::LDSUPERBYNAME_IMM8_ID16:
736 case EcmaOpcode::LDSUPERBYNAME_IMM16_ID16:
737 case EcmaOpcode::STSUPERBYNAME_IMM8_ID16_V8:
738 case EcmaOpcode::STSUPERBYNAME_IMM16_ID16_V8:
739 case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8:
740 case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8:
741 case EcmaOpcode::DEFINEFUNC_IMM8_ID16_IMM8:
742 case EcmaOpcode::DEFINEFUNC_IMM16_ID16_IMM8:
743 case EcmaOpcode::DEFINEMETHOD_IMM8_ID16_IMM8:
744 case EcmaOpcode::DEFINEMETHOD_IMM16_ID16_IMM8:
745 return;
746 default:
747 break;
748 }
749 }
750 isFastAccessor_ = true;
751 }
752 }
753 } // namespace panda::ecmascript
754