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