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