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