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