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