1/*
2 * Copyright (c) 2022-2024 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/deoptimizer/deoptimizer.h"
16
17#include <cmath>
18
19#include "ecmascript/dfx/stackinfo/js_stackinfo.h"
20#include "ecmascript/interpreter/slow_runtime_stub.h"
21#include "ecmascript/jit/jit.h"
22#include "ecmascript/stubs/runtime_stubs-inl.h"
23
24namespace panda::ecmascript {
25class FrameWriter {
26public:
27    explicit FrameWriter(Deoptimizier *deoptimizier) : thread_(deoptimizier->GetThread())
28    {
29        JSTaggedType *prevSp = const_cast<JSTaggedType *>(thread_->GetCurrentSPFrame());
30        start_ = top_ = EcmaInterpreter::GetInterpreterFrameEnd(thread_, prevSp);
31    }
32
33    void PushValue(JSTaggedType value)
34    {
35        *(--top_) = value;
36    }
37
38    void PushRawValue(uintptr_t value)
39    {
40        *(--top_) = value;
41    }
42
43    bool Reserve(size_t size)
44    {
45        return !thread_->DoStackOverflowCheck(top_ - size);
46    }
47
48    AsmInterpretedFrame *ReserveAsmInterpretedFrame()
49    {
50        auto frame = AsmInterpretedFrame::GetFrameFromSp(top_);
51        top_ = reinterpret_cast<JSTaggedType *>(frame);
52        return frame;
53    }
54
55    JSTaggedType *GetStart() const
56    {
57        return start_;
58    }
59
60    JSTaggedType *GetTop() const
61    {
62        return top_;
63    }
64
65    JSTaggedType *GetFirstFrame() const
66    {
67        return firstFrame_;
68    }
69
70    void RecordFirstFrame()
71    {
72        firstFrame_ = top_;
73    }
74
75    void ReviseValueByIndex(JSTaggedType value, size_t index)
76    {
77        ASSERT(index < static_cast<size_t>(start_ - top_));
78        *(top_ + index) = value;
79    }
80
81private:
82    JSThread *thread_ {nullptr};
83    JSTaggedType *start_ {nullptr};
84    JSTaggedType *top_ {nullptr};
85    JSTaggedType *firstFrame_ {nullptr};
86};
87
88Deoptimizier::Deoptimizier(JSThread *thread, size_t depth) : thread_(thread), inlineDepth_(depth)
89{
90    CalleeReg callreg;
91    numCalleeRegs_ = static_cast<size_t>(callreg.GetCallRegNum());
92    JSRuntimeOptions options = thread_->GetEcmaVM()->GetJSOptions();
93    traceDeopt_ = options.GetTraceDeopt();
94}
95
96void Deoptimizier::CollectVregs(const std::vector<kungfu::ARKDeopt>& deoptBundle, size_t shift)
97{
98    deoptVregs_.clear();
99    for (size_t i = 0; i < deoptBundle.size(); i++) {
100        ARKDeopt deopt = deoptBundle.at(i);
101        JSTaggedType v;
102        VRegId id = deopt.id;
103        if (std::holds_alternative<DwarfRegAndOffsetType>(deopt.value)) {
104            ASSERT(deopt.kind == LocationTy::Kind::INDIRECT);
105            auto value = std::get<DwarfRegAndOffsetType>(deopt.value);
106            DwarfRegType dwarfReg = value.first;
107            OffsetType offset = value.second;
108            ASSERT (dwarfReg == GCStackMapRegisters::FP || dwarfReg == GCStackMapRegisters::SP);
109            uintptr_t addr;
110            if (dwarfReg == GCStackMapRegisters::SP) {
111                addr = context_.callsiteSp + offset;
112            } else {
113                addr = context_.callsiteFp + offset;
114            }
115            v = *(reinterpret_cast<JSTaggedType *>(addr));
116        } else if (std::holds_alternative<LargeInt>(deopt.value)) {
117            ASSERT(deopt.kind == LocationTy::Kind::CONSTANTNDEX);
118            v = JSTaggedType(static_cast<int64_t>(std::get<LargeInt>(deopt.value)));
119        } else {
120            ASSERT(std::holds_alternative<IntType>(deopt.value));
121            ASSERT(deopt.kind == LocationTy::Kind::CONSTANT);
122            v = JSTaggedType(static_cast<int64_t>(std::get<IntType>(deopt.value)));
123        }
124        size_t curDepth = DecodeDeoptDepth(id, shift);
125        OffsetType vregId = static_cast<OffsetType>(DecodeVregIndex(id, shift));
126        if (vregId != static_cast<OffsetType>(SpecVregIndex::PC_OFFSET_INDEX)) {
127            deoptVregs_.insert({{curDepth, vregId}, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(v))});
128        } else {
129            pc_.insert({curDepth, static_cast<size_t>(v)});
130        }
131    }
132}
133
134// when AOT trigger deopt, frame layout as the following
135// * OptimizedJSFunctionFrame layout description as the following:
136//               +--------------------------+ ---------------
137//               |        ......            |               ^
138//               |        ......            |       callerFunction
139//               |        ......            |               |
140//               |--------------------------|               |
141//               |        args              |               v
142//               +--------------------------+ ---------------
143//               |       returnAddr         |               ^
144//               |--------------------------|               |
145//               |       callsiteFp         |               |
146//               |--------------------------|   OptimizedJSFunction  FrameType:OPTIMIZED_JS_FUNCTION_FRAME
147//               |       frameType          |               |
148//               |--------------------------|               |
149//               |       call-target        |               |
150//               |--------------------------|               |
151//               |       lexEnv             |               |
152//               |--------------------------|               |
153//               |       ...........        |               v
154//               +--------------------------+ ---------------
155//               |       returnAddr         |               ^
156//               |--------------------------|               |
157//               |       callsiteFp         |               |
158//               |--------------------------|   __llvm_deoptimize  FrameType:OPTIMIZED_FRAME
159//               |       frameType          |               |
160//               |--------------------------|               |
161//               |       No CalleeSave      |               |
162//               |       Registers          |               v
163//               +--------------------------+ ---------------
164//               |       returnAddr         |               ^
165//               |--------------------------|               |
166//               |       callsiteFp         |               |
167//               |--------------------------|   DeoptHandlerAsm  FrameType:ASM_BRIDGE_FRAME
168//               |       frameType          |               |
169//               |--------------------------|               |
170//               |       glue               |               |
171//               |--------------------------|               |
172//               | CalleeSave Registers     |               v
173//               +--------------------------+ ---------------
174//               |       .........          |               ^
175//               |       .........          |     CallRuntime   FrameType:LEAVE_FRAME
176//               |       .........          |               |
177//               |       .........          |               v
178//               |--------------------------| ---------------
179
180// After gathering the necessary information(After Call Runtime), frame layout after constructing
181// asminterpreterframe is shown as the following:
182//               +----------------------------------+---------+
183//               |        ......                    |         ^
184//               |        ......                    |   callerFunction
185//               |        ......                    |         |
186//               |----------------------------------|         |
187//               |        args                      |         v
188//               +----------------------------------+---------+
189//               |       returnAddr                 |         ^
190//               |----------------------------------|         |
191//               |       frameType                  |         |
192//               |----------------------------------|    ASM_INTERPRETER_BRIDGE_FRAME
193//               |       callsiteFp                 |         |
194//               |----------------------------------|         |
195//               |       ...........                |         v
196//               +----------------------------------+---------+
197//               |       returnAddr                 |
198//               |----------------------------------|
199//               |    argv[n-1]                     |
200//               |----------------------------------|
201//               |    ......                        |
202//               |----------------------------------|
203//               |    thisArg [maybe not exist]     |
204//               |----------------------------------|
205//               |    newTarget [maybe not exist]   |
206//               |----------------------------------|
207//               |    ......                        |
208//               |----------------------------------|
209//               |    Vregs [not exist in native]   |
210//               +----------------------------------+--------+
211//               |        .  .  .   .               |        ^
212//               |     InterpretedFrameBase         |        |
213//               |        .  .  .   .               |        |
214//               |----------------------------------|        |
215//               |    pc(bytecode addr)             |        |
216//               |----------------------------------|        |
217//               |    sp(current stack pointer)     |        |
218//               |----------------------------------|   AsmInterpretedFrame 0
219//               |    callSize                      |        |
220//               |----------------------------------|        |
221//               |    env                           |        |
222//               |----------------------------------|        |
223//               |    acc                           |        |
224//               |----------------------------------|        |
225//               |    thisObj                       |        |
226//               |----------------------------------|        |
227//               |    call-target                   |        v
228//               +----------------------------------+--------+
229//               |    argv[n-1]                     |
230//               |----------------------------------|
231//               |    ......                        |
232//               |----------------------------------|
233//               |    thisArg [maybe not exist]     |
234//               |----------------------------------|
235//               |    newTarget [maybe not exist]   |
236//               |----------------------------------|
237//               |    ......                        |
238//               |----------------------------------|
239//               |    Vregs [not exist in native]   |
240//               +----------------------------------+--------+
241//               |        .  .  .   .               |        ^
242//               |     InterpretedFrameBase         |        |
243//               |        .  .  .   .               |        |
244//               |----------------------------------|        |
245//               |    pc(bytecode addr)             |        |
246//               |----------------------------------|        |
247//               |    sp(current stack pointer)     |        |
248//               |----------------------------------|   AsmInterpretedFrame 1
249//               |    callSize                      |        |
250//               |----------------------------------|        |
251//               |    env                           |        |
252//               |----------------------------------|        |
253//               |    acc                           |        |
254//               |----------------------------------|        |
255//               |    thisObj                       |        |
256//               |----------------------------------|        |
257//               |    call-target                   |        v
258//               +----------------------------------+--------+
259//               |        .  .  .   .               |        ^
260//               |        .  .  .   .               |   AsmInterpretedFrame n
261//               |        .  .  .   .               |        v
262//               +----------------------------------+--------+
263
264template<class T>
265void Deoptimizier::AssistCollectDeoptBundleVec(FrameIterator &it, T &frame)
266{
267    CalleeRegAndOffsetVec calleeRegInfo;
268    frame->GetFuncCalleeRegAndOffset(it, calleeRegInfo);
269    context_.calleeRegAndOffset = calleeRegInfo;
270    context_.callsiteSp = it.GetCallSiteSp();
271    context_.callsiteFp = reinterpret_cast<uintptr_t>(it.GetSp());
272    auto preFrameSp = frame->ComputePrevFrameSp(it);
273    frameArgc_ = frame->GetArgc(preFrameSp);
274    frameArgvs_ = frame->GetArgv(preFrameSp);
275    stackContext_.callFrameTop_ = it.GetPrevFrameCallSiteSp();
276    stackContext_.returnAddr_ = frame->GetReturnAddr();
277    stackContext_.callerFp_ = reinterpret_cast<uintptr_t>(frame->GetPrevFrameFp());
278}
279
280void Deoptimizier::CollectDeoptBundleVec(std::vector<ARKDeopt>& deoptBundle)
281{
282    JSTaggedType *lastLeave = const_cast<JSTaggedType *>(thread_->GetLastLeaveFrame());
283    FrameIterator it(lastLeave, thread_);
284    // note: last deopt bridge frame is generated by DeoptHandlerAsm, callee Regs is grow from this frame
285    for (; !it.Done() && deoptBundle.empty(); it.Advance<GCVisitedFlag::DEOPT>()) {
286        FrameType type = it.GetFrameType();
287        switch (type) {
288            case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
289            case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
290                auto frame = it.GetFrame<OptimizedJSFunctionFrame>();
291                frame->GetDeoptBundleInfo(it, deoptBundle);
292                AssistCollectDeoptBundleVec(it, frame);
293                break;
294            }
295            case FrameType::FASTJIT_FUNCTION_FRAME:
296            case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
297                auto frame = it.GetFrame<FASTJITFunctionFrame>();
298                frame->GetDeoptBundleInfo(it, deoptBundle);
299                AssistCollectDeoptBundleVec(it, frame);
300                break;
301            }
302            case FrameType::ASM_BRIDGE_FRAME: {
303                auto sp = reinterpret_cast<uintptr_t*>(it.GetSp());
304                static constexpr size_t TYPE_GLUE_SLOT = 2; // 2: skip type & glue
305                sp -= TYPE_GLUE_SLOT;
306                calleeRegAddr_ = sp - numCalleeRegs_;
307                break;
308            }
309            case FrameType::OPTIMIZED_FRAME:
310            case FrameType::LEAVE_FRAME:
311                break;
312            default: {
313                LOG_FULL(FATAL) << "frame type error!";
314                UNREACHABLE();
315            }
316        }
317    }
318    ASSERT(!it.Done());
319}
320
321Method* Deoptimizier::GetMethod(JSTaggedValue &target)
322{
323    ECMAObject *callTarget = reinterpret_cast<ECMAObject*>(target.GetTaggedObject());
324    ASSERT(callTarget != nullptr);
325    Method *method = callTarget->GetCallTarget();
326    return method;
327}
328
329void Deoptimizier::RelocateCalleeSave()
330{
331    CalleeReg callreg;
332    for (auto &it: context_.calleeRegAndOffset) {
333        auto reg = it.first;
334        auto offset = it.second;
335        uintptr_t value = *(reinterpret_cast<uintptr_t *>(context_.callsiteFp + offset));
336        int order = callreg.FindCallRegOrder(reg);
337        calleeRegAddr_[order] = value;
338    }
339}
340
341bool Deoptimizier::CollectVirtualRegisters(JSTaggedValue callTarget, Method *method, FrameWriter *frameWriter,
342    size_t curDepth)
343{
344    int32_t actualNumArgs = 0;
345    int32_t declaredNumArgs = 0;
346    if (curDepth == 0) {
347        actualNumArgs = static_cast<int32_t>(GetDeoptValue(curDepth,
348            static_cast<int32_t>(SpecVregIndex::ACTUAL_ARGC_INDEX)).GetInt());
349        declaredNumArgs = static_cast<int32_t>(method->GetNumArgsWithCallField());
350    } else {
351        // inline method actualNumArgs equal to declaredNumArgs
352        actualNumArgs = static_cast<int32_t>(method->GetNumArgsWithCallField());
353        declaredNumArgs = static_cast<int32_t>(method->GetNumArgsWithCallField());
354    }
355
356    int32_t callFieldNumVregs = static_cast<int32_t>(method->GetNumVregsWithCallField());
357
358    // layout of frame:
359    // [maybe argc] [actual args] [reserved args] [call field virtual regs]
360
361    // [maybe argc]
362    bool isFastCall = JSFunctionBase::IsFastCallFromCallTarget(callTarget);
363    if (!isFastCall && declaredNumArgs != actualNumArgs) {
364        auto value = JSTaggedValue(actualNumArgs);
365        frameWriter->PushValue(value.GetRawData());
366    }
367    int32_t virtualIndex = declaredNumArgs + callFieldNumVregs +
368        static_cast<int32_t>(method->GetNumRevervedArgs()) - 1;
369    if (!frameWriter->Reserve(static_cast<size_t>(virtualIndex))) {
370        return false;
371    }
372    for (int32_t i = static_cast<int32_t>(declaredNumArgs - 1); i >= 0; i--) {
373        JSTaggedValue value = JSTaggedValue::Undefined();
374        // deopt value
375        if (HasDeoptValue(curDepth, virtualIndex)) {
376            value = GetDeoptValue(curDepth, virtualIndex);
377        }
378        frameWriter->PushValue(value.GetRawData());
379        virtualIndex--;
380    }
381
382    // [reserved args]
383    if (method->HaveThisWithCallField()) {
384        JSTaggedValue value = deoptVregs_.at(
385            {curDepth, static_cast<OffsetType>(SpecVregIndex::THIS_OBJECT_INDEX)}).GetTaggedValue();
386        frameWriter->PushValue(value.GetRawData());
387        virtualIndex--;
388    }
389    if (method->HaveNewTargetWithCallField()) {
390        JSTaggedValue value = deoptVregs_.at(
391            {curDepth, static_cast<OffsetType>(SpecVregIndex::NEWTARGET_INDEX)}).GetTaggedValue();
392        frameWriter->PushValue(value.GetRawData());
393        virtualIndex--;
394    }
395    if (method->HaveFuncWithCallField()) {
396        JSTaggedValue value = deoptVregs_.at(
397            {curDepth, static_cast<OffsetType>(SpecVregIndex::FUNC_INDEX)}).GetTaggedValue();
398        frameWriter->PushValue(value.GetRawData());
399        virtualIndex--;
400    }
401
402    // [call field virtual regs]
403    for (int32_t i = virtualIndex; i >= 0; i--) {
404        JSTaggedValue value = GetDeoptValue(curDepth, virtualIndex);
405        frameWriter->PushValue(value.GetRawData());
406        virtualIndex--;
407    }
408    // revise correct a0 - aN virtual regs , for example: ldobjbyname key; sta a2; update value to a2
409    //         +--------------------------+            ^
410    //         |       aN                 |            |
411    //         +--------------------------+            |
412    //         |       ...                |            |
413    //         +--------------------------+            |
414    //         |       a2(this)           |            |
415    //         +--------------------------+   revise correct vreg
416    //         |       a1(newtarget)      |            |
417    //         +--------------------------+            |
418    //         |       a0(func)           |            |
419    //         |--------------------------|            v
420    //         |       v0 - vN            |
421    //  sp --> |--------------------------|
422    int32_t vregsAndArgsNum = declaredNumArgs + callFieldNumVregs +
423        static_cast<int32_t>(method->GetNumRevervedArgs());
424    for (int32_t i = callFieldNumVregs; i < vregsAndArgsNum; i++) {
425        JSTaggedValue value = JSTaggedValue::Undefined();
426        if (HasDeoptValue(curDepth, i)) {
427            value = GetDeoptValue(curDepth, i);
428            frameWriter->ReviseValueByIndex(value.GetRawData(), i);
429        }
430    }
431    return true;
432}
433
434void Deoptimizier::Dump(JSTaggedValue callTarget, kungfu::DeoptType type, size_t depth)
435{
436    if (thread_->IsPGOProfilerEnable()) {
437        JSFunction *function = JSFunction::Cast(callTarget);
438        auto profileTypeInfo = function->GetProfileTypeInfo();
439        if (profileTypeInfo.IsUndefined()) {
440            SlowRuntimeStub::NotifyInlineCache(thread_, function);
441        }
442    }
443    if (traceDeopt_) {
444        std::string checkType = DisplayItems(type);
445        LOG_TRACE(INFO) << "Check Type: " << checkType;
446        std::string data = JsStackInfo::BuildJsStackTrace(thread_, true);
447        LOG_COMPILER(INFO) << "Deoptimize" << data;
448        const uint8_t *pc = GetMethod(callTarget)->GetBytecodeArray() + pc_.at(depth);
449        BytecodeInstruction inst(pc);
450        LOG_COMPILER(INFO) << inst;
451    }
452}
453
454std::string Deoptimizier::DisplayItems(DeoptType type)
455{
456    const std::map<DeoptType, const char *> strMap = {
457#define DEOPT_NAME_MAP(NAME, TYPE) {DeoptType::TYPE, #NAME},
458        GATE_META_DATA_DEOPT_REASON(DEOPT_NAME_MAP)
459#undef DEOPT_NAME_MAP
460    };
461    if (strMap.count(type) > 0) {
462        return strMap.at(type);
463    }
464    return "DeoptType-" + std::to_string(static_cast<uint8_t>(type));
465}
466
467// layout of frameWriter
468//   |--------------------------| --------------> start(n)
469//   |          args            |
470//   |          this            |
471//   |        newTarget         |
472//   |       callTarget         |
473//   |          vregs           |
474//   |---------------------------
475//   |       ASM Interpreter    |
476//   +--------------------------+ --------------> end(n)
477//   |        outputcounts      |          outputcounts = end(n) - start(n)
478//   |--------------------------| --------------> start(n-1)
479//   |          args            |
480//   |          this            |
481//   |        newTarget         |
482//   |       callTarget         |
483//   |          vregs           |
484//   |-------------------------------------------
485//   |       ASM Interpreter    |
486//   +--------------------------+ --------------> end(n-1)
487//   |        outputcounts      |           outputcounts = end(n-1) - start(n-1)
488//   |--------------------------| --------------> start(n-1)
489//   |       ......             |
490//   +--------------------------+ ---------------
491//   |        callerFp_         |               ^
492//   |       returnAddr_        |          stackContext
493//   |      callFrameTop_       |               |
494//   |       inlineDepth        |               v
495//   |--------------------------| ---------------
496
497JSTaggedType Deoptimizier::ConstructAsmInterpretFrame()
498{
499    FrameWriter frameWriter(this);
500    // Push asm interpreter frame
501    for (int32_t curDepth = static_cast<int32_t>(inlineDepth_); curDepth >= 0; curDepth--) {
502        auto start = frameWriter.GetTop();
503        JSTaggedValue callTarget = GetDeoptValue(curDepth, static_cast<int32_t>(SpecVregIndex::FUNC_INDEX));
504        auto method = GetMethod(callTarget);
505        if (!CollectVirtualRegisters(callTarget, method, &frameWriter, curDepth)) {
506            return JSTaggedValue::Exception().GetRawData();
507        }
508        AsmInterpretedFrame *statePtr = frameWriter.ReserveAsmInterpretedFrame();
509        const uint8_t *resumePc = method->GetBytecodeArray() + pc_.at(curDepth);
510        JSTaggedValue thisObj = GetDeoptValue(curDepth, static_cast<int32_t>(SpecVregIndex::THIS_OBJECT_INDEX));
511        auto acc = GetDeoptValue(curDepth, static_cast<int32_t>(SpecVregIndex::ACC_INDEX));
512        statePtr->function = callTarget;
513        statePtr->acc = acc;
514        statePtr->env = GetDeoptValue(curDepth, static_cast<int32_t>(SpecVregIndex::ENV_INDEX));
515        statePtr->callSize = GetCallSize(curDepth, resumePc);
516        statePtr->fp = 0;  // need update
517        statePtr->thisObj = thisObj;
518        statePtr->pc = resumePc;
519        // -uintptr_t skip lr
520        if (curDepth == 0) {
521            statePtr->base.prev = reinterpret_cast<JSTaggedType *>(stackContext_.callFrameTop_ - sizeof(uintptr_t));
522        } else {
523            statePtr->base.prev = 0; // need update
524        }
525
526        statePtr->base.type = FrameType::ASM_INTERPRETER_FRAME;
527
528        // construct stack context
529        auto end = frameWriter.GetTop();
530        auto outputCount = start - end;
531        frameWriter.PushRawValue(outputCount);
532    }
533
534    RelocateCalleeSave();
535
536    frameWriter.PushRawValue(stackContext_.callerFp_);
537    frameWriter.PushRawValue(stackContext_.returnAddr_);
538    frameWriter.PushRawValue(stackContext_.callFrameTop_);
539    frameWriter.PushRawValue(inlineDepth_);
540    return reinterpret_cast<JSTaggedType>(frameWriter.GetTop());
541}
542
543void Deoptimizier::ResetJitHotness(JSFunction *jsFunc) const
544{
545    if (jsFunc->GetMachineCode().IsMachineCodeObject()) {
546        JSTaggedValue profileTypeInfoVal = jsFunc->GetProfileTypeInfo();
547        if (!profileTypeInfoVal.IsUndefined()) {
548            ProfileTypeInfo *profileTypeInfo = ProfileTypeInfo::Cast(profileTypeInfoVal.GetTaggedObject());
549            profileTypeInfo->SetJitHotnessCnt(0);
550            constexpr uint16_t thresholdStep = 4;
551            constexpr uint16_t thresholdLimit = ProfileTypeInfo::JIT_DISABLE_FLAG / thresholdStep;
552            uint16_t threshold = profileTypeInfo->GetJitHotnessThreshold();
553            threshold = threshold >= thresholdLimit ? ProfileTypeInfo::JIT_DISABLE_FLAG : threshold * thresholdStep;
554            profileTypeInfo->SetJitHotnessThreshold(threshold);
555            ProfileTypeInfoCell::Cast(jsFunc->GetRawProfileTypeInfo())->SetMachineCode(thread_, JSTaggedValue::Hole());
556            Method *method = Method::Cast(jsFunc->GetMethod().GetTaggedObject());
557            LOG_JIT(DEBUG) << "reset jit hotness for func: " << method->GetMethodName() << ", threshold:" << threshold;
558        }
559    }
560}
561
562void Deoptimizier::ClearCompiledCodeStatusWhenDeopt(JSFunction *func, Method *method)
563{
564    if (func->GetMachineCode().IsMachineCodeObject()) {
565        Jit::GetInstance()->GetJitDfx()->SetJitDeoptCount();
566    }
567    if (func->IsCompiledCode()) {
568        bool isFastCall = func->IsCompiledFastCall();  // get this flag before clear it
569        uintptr_t entry =
570            isFastCall ? thread_->GetRTInterface(kungfu::RuntimeStubCSigns::ID_FastCallToAsmInterBridge)
571                       : thread_->GetRTInterface(kungfu::RuntimeStubCSigns::ID_AOTCallToAsmInterBridge);
572        func->SetCodeEntry(entry);
573        method->ClearAOTStatusWhenDeopt(entry);
574        func->ClearCompiledCodeFlags();
575        ResetJitHotness(func);
576        func->ClearMachineCode(thread_);
577    }  // Do not change the func code entry if the method is not aot or deopt has happened already
578}
579
580void Deoptimizier::UpdateAndDumpDeoptInfo(kungfu::DeoptType type)
581{
582    // depth records the number of layers of nested calls when deopt occurs
583    for (size_t i = 0; i <= inlineDepth_; i++) {
584        JSTaggedValue callTarget = GetDeoptValue(i, static_cast<int32_t>(SpecVregIndex::FUNC_INDEX));
585        auto func = JSFunction::Cast(callTarget.GetTaggedObject());
586        if (func->GetMachineCode().IsMachineCodeObject()) {
587            MachineCode *machineCode = MachineCode::Cast(func->GetMachineCode().GetTaggedObject());
588            if (type != kungfu::DeoptType::OSRLOOPEXIT &&
589                machineCode->GetOSROffset() != MachineCode::INVALID_OSR_OFFSET) {
590                machineCode->SetOsrDeoptFlag(true);
591            }
592        }
593        auto method = GetMethod(callTarget);
594        if (i == inlineDepth_) {
595            Dump(callTarget, type, i);
596        }
597        ASSERT(thread_ != nullptr);
598        uint8_t deoptThreshold = method->GetDeoptThreshold();
599        if (deoptThreshold > 0) {
600            method->SetDeoptType(type);
601            method->SetDeoptThreshold(--deoptThreshold);
602        } else {
603            ClearCompiledCodeStatusWhenDeopt(func, method);
604        }
605    }
606}
607
608// call instructions need compute jumpSize
609size_t Deoptimizier::GetCallSize(size_t curDepth, const uint8_t *resumePc)
610{
611    if (inlineDepth_ > 0 && curDepth != inlineDepth_) {
612        auto op = BytecodeInstruction(resumePc).GetOpcode();
613        size_t jumpSize = BytecodeInstruction::Size(op);
614        return jumpSize;
615    }
616    return 0;
617}
618
619int32_t Deoptimizier::EncodeDeoptVregIndex(int32_t index, size_t depth, size_t shift)
620{
621    if (index >= 0) {
622        return (index << shift) | depth;
623    }
624    return -((-index << shift) | depth);
625}
626
627size_t Deoptimizier::ComputeShift(size_t depth)
628{
629    size_t shift = 0;
630    if (depth != 0) {
631        shift = std::floor(std::log2(depth)) + 1;
632    }
633    return shift;
634}
635
636int32_t Deoptimizier::DecodeVregIndex(OffsetType id, size_t shift)
637{
638    if (id >= 0) {
639        return id >> shift;
640    }
641    return -((-id) >> shift);
642}
643
644size_t Deoptimizier::DecodeDeoptDepth(OffsetType id, size_t shift)
645{
646    size_t mask = (1 << shift) - 1;
647    if (id >= 0) {
648        return id & mask;
649    }
650    return (-id) & mask;
651}
652}  // namespace panda::ecmascript
653