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 23namespace panda::ecmascript::kungfu { 24void 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 38void 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 50void 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 61void 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 103void 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 182bool 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 209void 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 254bool 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 270void 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 307void 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 340GateRef 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 375void 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 385GateRef 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 409void 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 437GateRef 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 445void 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 468void 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 486void 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 518void 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 537void 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 564void TSInlineLowering::InlineCheck(InlineTypeInfoAccessor &info) 565{ 566 if (info.IsNormalCall()) { 567 InlineFuncCheck(info); 568 } else { 569 InlineAccessorCheck(info); 570 } 571} 572 573void 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 584void 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 593bool 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 604void 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 613void 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 628size_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 637bool 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 660void 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 670void 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 682GateRef 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 692GateRef 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 702GateRef TSInlineLowering::GetFrameState(InlineTypeInfoAccessor &info) 703{ 704 GateRef gate = info.GetCallGate(); 705 ASSERT(info.IsCallAccessor() || info.IsNormalCall()); 706 return acc_.GetFrameState(gate); 707} 708 709GateRef TSInlineLowering::GetFrameArgs(InlineTypeInfoAccessor &info) 710{ 711 GateRef frameState = GetFrameState(info); 712 return acc_.GetValueIn(frameState); 713} 714 715void 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 724uint32_t TSInlineLowering::GetAccessorConstpoolId(InlineTypeInfoAccessor &info) 725{ 726 ASSERT(info.IsCallAccessor()); 727 uint32_t inlineMethodOffset = info.GetCallMethodId(); 728 return ptManager_->GetConstantPoolIDByMethodOffset(inlineMethodOffset); 729} 730 731bool 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 759void 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