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