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 
21 namespace panda::ecmascript {
22 // Build the infrastructure and wait for TS to invoke.
SetMonoConstuctCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId, JSTaggedValue newTarget, JSTaggedValue initialHClass)23 bool 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 
SetPolyConstuctCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId, uint8_t length, JSTaggedValue newTargetArray, JSTaggedValue initialHClassArray)37 bool 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 
CheckPolyInvokeCache(JSTaggedValue cachedArray, JSTaggedValue func)64 JSTaggedValue 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 
Construct(JSThread *thread, JSTaggedValue firstValue, JSTaggedValue secondValue, JSTaggedValue ctor, JSTaggedValue newTarget, uint16_t firstArgIdx, uint16_t length)80 JSTaggedValue 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
SetMonoInlineCallCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId, JSTaggedValue callee)122 bool 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 
SetPolyInlineCallCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId, uint8_t length, JSTaggedValue calleeArray)136 bool 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 
DecideCanBeInlined(Method *method)161 bool 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