1/*
2 * Copyright (c) 2021-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
16#include "ecmascript/ic/invoke_cache.h"
17#include "ecmascript/interpreter/frame_handler.h"
18#include "ecmascript/interpreter/interpreter.h"
19
20
21namespace panda::ecmascript {
22// Build the infrastructure and wait for TS to invoke.
23bool InvokeCache::SetMonoConstuctCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId,
24                                           JSTaggedValue newTarget, JSTaggedValue initialHClass)
25{
26    // only cache class constructor
27    if (UNLIKELY(!newTarget.IsClassConstructor())) {
28        return false;
29    }
30
31    profileTypeInfo->Set(thread, slotId, newTarget);
32    profileTypeInfo->Set(thread, slotId + 1, initialHClass);
33
34    return true;
35}
36
37bool InvokeCache::SetPolyConstuctCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId,
38                                           uint8_t length, JSTaggedValue newTargetArray,
39                                           JSTaggedValue initialHClassArray)
40{
41    ASSERT(length <= POLY_CASE_NUM && newTargetArray.IsTaggedArray() && initialHClassArray.IsTaggedArray());
42
43    JSHandle<TaggedArray> profileTypeInfoArr(thread, profileTypeInfo);
44    JSHandle<TaggedArray> newTargetArr(thread, newTargetArray);
45    JSHandle<TaggedArray> initialHClassArr(thread, initialHClassArray);
46
47    auto factory = thread->GetEcmaVM()->GetFactory();
48    constexpr uint8_t step = 2;
49    JSHandle<TaggedArray> newArray = factory->NewTaggedArray(length * step);  // 2: newTarget and hclass
50
51    for (uint8_t index = 0; index < length; ++index) {
52        ASSERT(newTargetArr->Get(index).IsClassConstructor());
53
54        newArray->Set(thread, index * step, newTargetArr->Get(index));
55        newArray->Set(thread, index * step + 1, initialHClassArr->Get(index));
56    }
57
58    profileTypeInfoArr->Set(thread, slotId, newArray);
59    profileTypeInfoArr->Set(thread, slotId + 1, JSTaggedValue::Hole());
60
61    return true;
62}
63
64JSTaggedValue InvokeCache::CheckPolyInvokeCache(JSTaggedValue cachedArray, JSTaggedValue func)
65{
66    ASSERT(cachedArray.IsTaggedArray());
67    TaggedArray *array = TaggedArray::Cast(cachedArray.GetTaggedObject());
68    uint32_t length = array->GetLength();
69    for (uint32_t index = 0; index < length; index += 2) {  // 2: means one ic, two slot
70        auto result = array->Get(index);
71        if (JSFunction::Cast(result.GetTaggedObject())->GetMethod() ==
72            JSFunction::Cast(func.GetTaggedObject())->GetMethod()) {
73            return array->Get(index + 1);
74        }
75    }
76
77    return JSTaggedValue::Hole();
78}
79
80JSTaggedValue InvokeCache::Construct(JSThread *thread, JSTaggedValue firstValue, JSTaggedValue secondValue,
81                                     JSTaggedValue ctor, JSTaggedValue newTarget, uint16_t firstArgIdx, uint16_t length)
82{
83    // ic miss
84    if (UNLIKELY(!firstValue.IsHeapObject())) {
85        return JSTaggedValue::Hole();
86    }
87
88    // gc protection
89    JSHandle<JSFunction> constructor(thread, ctor);
90    JSHandle<JSFunction> newTgt(thread, newTarget);
91
92    JSHandle<JSHClass> instanceHClass;
93    // monomorphic
94    if (LIKELY(firstValue.IsJSFunction() &&
95        newTgt->GetMethod() == JSFunction::Cast(firstValue.GetTaggedObject())->GetMethod())) {
96        instanceHClass = JSHandle<JSHClass>(thread, JSHClass::Cast(secondValue.GetTaggedObject()));
97    } else {
98        // polymorphic
99        ASSERT(firstValue.IsTaggedArray());
100        JSTaggedValue polyCache = CheckPolyInvokeCache(firstValue, newTarget);
101        if (UNLIKELY(polyCache.IsHole())) {
102            return JSTaggedValue::Hole();
103        }
104        instanceHClass = JSHandle<JSHClass>(thread, JSHClass::Cast(polyCache.GetTaggedObject()));
105    }
106
107    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
108    JSHandle<JSObject> obj = factory->NewJSObject(instanceHClass);
109    EcmaRuntimeCallInfo *info =
110        EcmaInterpreter::NewRuntimeCallInfo(thread, JSHandle<JSTaggedValue>(constructor), JSHandle<JSTaggedValue>(obj),
111        JSHandle<JSTaggedValue>(newTgt), length);
112    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
113    FrameHandler frameHandler(thread);
114    for (size_t i = 0; i < length; i++) {
115        info->SetCallArg(i, frameHandler.GetVRegValue(firstArgIdx + i));
116    }
117    EcmaInterpreter::Execute(info);
118    return obj.GetTaggedValue();
119}
120
121// just identify simple callee case which can be inlined, the implement of inline need wait TS AOT
122bool InvokeCache::SetMonoInlineCallCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId,
123                                             JSTaggedValue callee)
124{
125    ASSERT(callee.IsJSFunction());
126    Method *calleeMethod = JSFunction::Cast(callee.GetTaggedObject())->GetCallTarget();
127    if (DecideCanBeInlined(calleeMethod)) {
128        profileTypeInfo->Set(thread, slotId, callee);
129        return true;
130    }
131
132    profileTypeInfo->Set(thread, slotId, JSTaggedValue::Hole());
133    return false;
134}
135
136bool InvokeCache::SetPolyInlineCallCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId,
137                                             uint8_t length, JSTaggedValue calleeArray)
138{
139    ASSERT(calleeArray.IsTaggedArray() && length >= MONO_CASE_NUM && length <= POLY_CASE_NUM);
140    JSHandle<TaggedArray> calleeArr(thread, calleeArray);
141    ASSERT(calleeArr->GetLength() == length);
142    JSHandle<TaggedArray> profileTypeInfoArr(thread, profileTypeInfo);
143
144    auto factory = thread->GetEcmaVM()->GetFactory();
145    JSHandle<TaggedArray> newArray = factory->NewTaggedArray(length);
146
147    for (uint8_t index = 0; index < length; ++index) {
148        JSTaggedValue calleeElement = calleeArr->Get(index);
149        Method *calleeMethod = JSFunction::Cast(calleeElement.GetTaggedObject())->GetCallTarget();
150        if (DecideCanBeInlined(calleeMethod)) {
151            newArray->Set(thread, index, calleeElement);
152        } else {
153            newArray->Set(thread, index, JSTaggedValue::Hole());
154        }
155    }
156
157    profileTypeInfoArr->Set(thread, slotId, newArray);
158    return true;
159}
160
161bool InvokeCache::DecideCanBeInlined(Method *method)
162{
163    constexpr uint32_t MAX_INLINED_BYTECODE_SIZE = 128;
164    uint32_t bcSize = method->GetCodeSize();
165    return (bcSize > 0 && bcSize < MAX_INLINED_BYTECODE_SIZE);  // 0 is invalid
166}
167}  // namespace panda::ecmascript
168