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