14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#include "ecmascript/ic/invoke_cache.h"
174514f5e3Sopenharmony_ci#include "ecmascript/interpreter/frame_handler.h"
184514f5e3Sopenharmony_ci#include "ecmascript/interpreter/interpreter.h"
194514f5e3Sopenharmony_ci
204514f5e3Sopenharmony_ci
214514f5e3Sopenharmony_cinamespace panda::ecmascript {
224514f5e3Sopenharmony_ci// Build the infrastructure and wait for TS to invoke.
234514f5e3Sopenharmony_cibool InvokeCache::SetMonoConstuctCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId,
244514f5e3Sopenharmony_ci                                           JSTaggedValue newTarget, JSTaggedValue initialHClass)
254514f5e3Sopenharmony_ci{
264514f5e3Sopenharmony_ci    // only cache class constructor
274514f5e3Sopenharmony_ci    if (UNLIKELY(!newTarget.IsClassConstructor())) {
284514f5e3Sopenharmony_ci        return false;
294514f5e3Sopenharmony_ci    }
304514f5e3Sopenharmony_ci
314514f5e3Sopenharmony_ci    profileTypeInfo->Set(thread, slotId, newTarget);
324514f5e3Sopenharmony_ci    profileTypeInfo->Set(thread, slotId + 1, initialHClass);
334514f5e3Sopenharmony_ci
344514f5e3Sopenharmony_ci    return true;
354514f5e3Sopenharmony_ci}
364514f5e3Sopenharmony_ci
374514f5e3Sopenharmony_cibool InvokeCache::SetPolyConstuctCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId,
384514f5e3Sopenharmony_ci                                           uint8_t length, JSTaggedValue newTargetArray,
394514f5e3Sopenharmony_ci                                           JSTaggedValue initialHClassArray)
404514f5e3Sopenharmony_ci{
414514f5e3Sopenharmony_ci    ASSERT(length <= POLY_CASE_NUM && newTargetArray.IsTaggedArray() && initialHClassArray.IsTaggedArray());
424514f5e3Sopenharmony_ci
434514f5e3Sopenharmony_ci    JSHandle<TaggedArray> profileTypeInfoArr(thread, profileTypeInfo);
444514f5e3Sopenharmony_ci    JSHandle<TaggedArray> newTargetArr(thread, newTargetArray);
454514f5e3Sopenharmony_ci    JSHandle<TaggedArray> initialHClassArr(thread, initialHClassArray);
464514f5e3Sopenharmony_ci
474514f5e3Sopenharmony_ci    auto factory = thread->GetEcmaVM()->GetFactory();
484514f5e3Sopenharmony_ci    constexpr uint8_t step = 2;
494514f5e3Sopenharmony_ci    JSHandle<TaggedArray> newArray = factory->NewTaggedArray(length * step);  // 2: newTarget and hclass
504514f5e3Sopenharmony_ci
514514f5e3Sopenharmony_ci    for (uint8_t index = 0; index < length; ++index) {
524514f5e3Sopenharmony_ci        ASSERT(newTargetArr->Get(index).IsClassConstructor());
534514f5e3Sopenharmony_ci
544514f5e3Sopenharmony_ci        newArray->Set(thread, index * step, newTargetArr->Get(index));
554514f5e3Sopenharmony_ci        newArray->Set(thread, index * step + 1, initialHClassArr->Get(index));
564514f5e3Sopenharmony_ci    }
574514f5e3Sopenharmony_ci
584514f5e3Sopenharmony_ci    profileTypeInfoArr->Set(thread, slotId, newArray);
594514f5e3Sopenharmony_ci    profileTypeInfoArr->Set(thread, slotId + 1, JSTaggedValue::Hole());
604514f5e3Sopenharmony_ci
614514f5e3Sopenharmony_ci    return true;
624514f5e3Sopenharmony_ci}
634514f5e3Sopenharmony_ci
644514f5e3Sopenharmony_ciJSTaggedValue InvokeCache::CheckPolyInvokeCache(JSTaggedValue cachedArray, JSTaggedValue func)
654514f5e3Sopenharmony_ci{
664514f5e3Sopenharmony_ci    ASSERT(cachedArray.IsTaggedArray());
674514f5e3Sopenharmony_ci    TaggedArray *array = TaggedArray::Cast(cachedArray.GetTaggedObject());
684514f5e3Sopenharmony_ci    uint32_t length = array->GetLength();
694514f5e3Sopenharmony_ci    for (uint32_t index = 0; index < length; index += 2) {  // 2: means one ic, two slot
704514f5e3Sopenharmony_ci        auto result = array->Get(index);
714514f5e3Sopenharmony_ci        if (JSFunction::Cast(result.GetTaggedObject())->GetMethod() ==
724514f5e3Sopenharmony_ci            JSFunction::Cast(func.GetTaggedObject())->GetMethod()) {
734514f5e3Sopenharmony_ci            return array->Get(index + 1);
744514f5e3Sopenharmony_ci        }
754514f5e3Sopenharmony_ci    }
764514f5e3Sopenharmony_ci
774514f5e3Sopenharmony_ci    return JSTaggedValue::Hole();
784514f5e3Sopenharmony_ci}
794514f5e3Sopenharmony_ci
804514f5e3Sopenharmony_ciJSTaggedValue InvokeCache::Construct(JSThread *thread, JSTaggedValue firstValue, JSTaggedValue secondValue,
814514f5e3Sopenharmony_ci                                     JSTaggedValue ctor, JSTaggedValue newTarget, uint16_t firstArgIdx, uint16_t length)
824514f5e3Sopenharmony_ci{
834514f5e3Sopenharmony_ci    // ic miss
844514f5e3Sopenharmony_ci    if (UNLIKELY(!firstValue.IsHeapObject())) {
854514f5e3Sopenharmony_ci        return JSTaggedValue::Hole();
864514f5e3Sopenharmony_ci    }
874514f5e3Sopenharmony_ci
884514f5e3Sopenharmony_ci    // gc protection
894514f5e3Sopenharmony_ci    JSHandle<JSFunction> constructor(thread, ctor);
904514f5e3Sopenharmony_ci    JSHandle<JSFunction> newTgt(thread, newTarget);
914514f5e3Sopenharmony_ci
924514f5e3Sopenharmony_ci    JSHandle<JSHClass> instanceHClass;
934514f5e3Sopenharmony_ci    // monomorphic
944514f5e3Sopenharmony_ci    if (LIKELY(firstValue.IsJSFunction() &&
954514f5e3Sopenharmony_ci        newTgt->GetMethod() == JSFunction::Cast(firstValue.GetTaggedObject())->GetMethod())) {
964514f5e3Sopenharmony_ci        instanceHClass = JSHandle<JSHClass>(thread, JSHClass::Cast(secondValue.GetTaggedObject()));
974514f5e3Sopenharmony_ci    } else {
984514f5e3Sopenharmony_ci        // polymorphic
994514f5e3Sopenharmony_ci        ASSERT(firstValue.IsTaggedArray());
1004514f5e3Sopenharmony_ci        JSTaggedValue polyCache = CheckPolyInvokeCache(firstValue, newTarget);
1014514f5e3Sopenharmony_ci        if (UNLIKELY(polyCache.IsHole())) {
1024514f5e3Sopenharmony_ci            return JSTaggedValue::Hole();
1034514f5e3Sopenharmony_ci        }
1044514f5e3Sopenharmony_ci        instanceHClass = JSHandle<JSHClass>(thread, JSHClass::Cast(polyCache.GetTaggedObject()));
1054514f5e3Sopenharmony_ci    }
1064514f5e3Sopenharmony_ci
1074514f5e3Sopenharmony_ci    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1084514f5e3Sopenharmony_ci    JSHandle<JSObject> obj = factory->NewJSObject(instanceHClass);
1094514f5e3Sopenharmony_ci    EcmaRuntimeCallInfo *info =
1104514f5e3Sopenharmony_ci        EcmaInterpreter::NewRuntimeCallInfo(thread, JSHandle<JSTaggedValue>(constructor), JSHandle<JSTaggedValue>(obj),
1114514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue>(newTgt), length);
1124514f5e3Sopenharmony_ci    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1134514f5e3Sopenharmony_ci    FrameHandler frameHandler(thread);
1144514f5e3Sopenharmony_ci    for (size_t i = 0; i < length; i++) {
1154514f5e3Sopenharmony_ci        info->SetCallArg(i, frameHandler.GetVRegValue(firstArgIdx + i));
1164514f5e3Sopenharmony_ci    }
1174514f5e3Sopenharmony_ci    EcmaInterpreter::Execute(info);
1184514f5e3Sopenharmony_ci    return obj.GetTaggedValue();
1194514f5e3Sopenharmony_ci}
1204514f5e3Sopenharmony_ci
1214514f5e3Sopenharmony_ci// just identify simple callee case which can be inlined, the implement of inline need wait TS AOT
1224514f5e3Sopenharmony_cibool InvokeCache::SetMonoInlineCallCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId,
1234514f5e3Sopenharmony_ci                                             JSTaggedValue callee)
1244514f5e3Sopenharmony_ci{
1254514f5e3Sopenharmony_ci    ASSERT(callee.IsJSFunction());
1264514f5e3Sopenharmony_ci    Method *calleeMethod = JSFunction::Cast(callee.GetTaggedObject())->GetCallTarget();
1274514f5e3Sopenharmony_ci    if (DecideCanBeInlined(calleeMethod)) {
1284514f5e3Sopenharmony_ci        profileTypeInfo->Set(thread, slotId, callee);
1294514f5e3Sopenharmony_ci        return true;
1304514f5e3Sopenharmony_ci    }
1314514f5e3Sopenharmony_ci
1324514f5e3Sopenharmony_ci    profileTypeInfo->Set(thread, slotId, JSTaggedValue::Hole());
1334514f5e3Sopenharmony_ci    return false;
1344514f5e3Sopenharmony_ci}
1354514f5e3Sopenharmony_ci
1364514f5e3Sopenharmony_cibool InvokeCache::SetPolyInlineCallCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId,
1374514f5e3Sopenharmony_ci                                             uint8_t length, JSTaggedValue calleeArray)
1384514f5e3Sopenharmony_ci{
1394514f5e3Sopenharmony_ci    ASSERT(calleeArray.IsTaggedArray() && length >= MONO_CASE_NUM && length <= POLY_CASE_NUM);
1404514f5e3Sopenharmony_ci    JSHandle<TaggedArray> calleeArr(thread, calleeArray);
1414514f5e3Sopenharmony_ci    ASSERT(calleeArr->GetLength() == length);
1424514f5e3Sopenharmony_ci    JSHandle<TaggedArray> profileTypeInfoArr(thread, profileTypeInfo);
1434514f5e3Sopenharmony_ci
1444514f5e3Sopenharmony_ci    auto factory = thread->GetEcmaVM()->GetFactory();
1454514f5e3Sopenharmony_ci    JSHandle<TaggedArray> newArray = factory->NewTaggedArray(length);
1464514f5e3Sopenharmony_ci
1474514f5e3Sopenharmony_ci    for (uint8_t index = 0; index < length; ++index) {
1484514f5e3Sopenharmony_ci        JSTaggedValue calleeElement = calleeArr->Get(index);
1494514f5e3Sopenharmony_ci        Method *calleeMethod = JSFunction::Cast(calleeElement.GetTaggedObject())->GetCallTarget();
1504514f5e3Sopenharmony_ci        if (DecideCanBeInlined(calleeMethod)) {
1514514f5e3Sopenharmony_ci            newArray->Set(thread, index, calleeElement);
1524514f5e3Sopenharmony_ci        } else {
1534514f5e3Sopenharmony_ci            newArray->Set(thread, index, JSTaggedValue::Hole());
1544514f5e3Sopenharmony_ci        }
1554514f5e3Sopenharmony_ci    }
1564514f5e3Sopenharmony_ci
1574514f5e3Sopenharmony_ci    profileTypeInfoArr->Set(thread, slotId, newArray);
1584514f5e3Sopenharmony_ci    return true;
1594514f5e3Sopenharmony_ci}
1604514f5e3Sopenharmony_ci
1614514f5e3Sopenharmony_cibool InvokeCache::DecideCanBeInlined(Method *method)
1624514f5e3Sopenharmony_ci{
1634514f5e3Sopenharmony_ci    constexpr uint32_t MAX_INLINED_BYTECODE_SIZE = 128;
1644514f5e3Sopenharmony_ci    uint32_t bcSize = method->GetCodeSize();
1654514f5e3Sopenharmony_ci    return (bcSize > 0 && bcSize < MAX_INLINED_BYTECODE_SIZE);  // 0 is invalid
1664514f5e3Sopenharmony_ci}
1674514f5e3Sopenharmony_ci}  // namespace panda::ecmascript
168