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/profile_type_info.h"
17
18 #include "ecmascript/ic/ic_handler.h"
19 #include "ecmascript/js_function.h"
20 #include "ecmascript/js_tagged_value.h"
21 #include "ecmascript/tagged_array-inl.h"
22 #include "libpandabase/macros.h"
23
24 namespace panda::ecmascript {
AddElementHandler(JSHandle<JSTaggedValue> hclass,JSHandle<JSTaggedValue> handler) const25 void ProfileTypeAccessor::AddElementHandler(JSHandle<JSTaggedValue> hclass, JSHandle<JSTaggedValue> handler) const
26 {
27 ALLOW_LOCAL_TO_SHARE_WEAK_REF_HANDLE;
28 auto profileData = profileTypeInfo_->GetIcSlot(thread_, slotId_);
29 ASSERT(!profileData.IsHole());
30 auto index = slotId_;
31 if (profileData.IsUndefined()) {
32 profileTypeInfo_->SetMultiIcSlotLocked(thread_, index, GetWeakRef(hclass.GetTaggedValue()),
33 index + 1, handler.GetTaggedValue());
34 return;
35 }
36 // clear key ic
37 if (!profileData.IsWeak() && (profileData.IsString() || profileData.IsSymbol())) {
38 profileTypeInfo_->SetMultiIcSlotLocked(thread_, index, GetWeakRef(hclass.GetTaggedValue()),
39 index + 1, handler.GetTaggedValue());
40 return;
41 }
42 AddHandlerWithoutKey(hclass, handler);
43 }
44
AddWithoutKeyPoly(JSHandle<JSTaggedValue> hclass,JSHandle<JSTaggedValue> handler,uint32_t index,JSTaggedValue profileData,JSHandle<JSTaggedValue> keyForMegaIC,MegaICCache::MegaICKind kind) const45 void ProfileTypeAccessor::AddWithoutKeyPoly(JSHandle<JSTaggedValue> hclass, JSHandle<JSTaggedValue> handler,
46 uint32_t index, JSTaggedValue profileData,
47 JSHandle<JSTaggedValue> keyForMegaIC, MegaICCache::MegaICKind kind) const
48 {
49 ASSERT(profileTypeInfo_->GetIcSlot(thread_, index + 1).IsHole());
50 JSHandle<TaggedArray> arr(thread_, profileData);
51 const uint32_t step = 2;
52 uint32_t newLen = arr->GetLength() + step;
53 if (newLen > CACHE_MAX_LEN) {
54 if (!enableICMega_ || keyForMegaIC.IsEmpty() || !keyForMegaIC->IsString()) {
55 profileTypeInfo_->SetMultiIcSlotLocked(thread_, index, JSTaggedValue::Hole(), index + 1,
56 JSTaggedValue::Hole());
57 return;
58 }
59 // The keyForMegaIC must be a String to ensure fast subsequent reads; assembly code will access using
60 // String.
61 ASSERT(keyForMegaIC->IsString());
62 ASSERT(kind != MegaICCache::None);
63 MegaICCache *cache = nullptr;
64 if (kind == MegaICCache::Load) {
65 cache = thread_->GetLoadMegaICCache();
66 } else {
67 cache = thread_->GetStoreMegaICCache();
68 }
69
70 uint32_t i = 0;
71 for (; i < arr->GetLength(); i += step) {
72 if (arr->Get(thread_, i) == JSTaggedValue::Undefined()) {
73 continue;
74 }
75 cache->Set(JSHClass::Cast(arr->Get(thread_, i).GetWeakReferentUnChecked()), keyForMegaIC.GetTaggedValue(),
76 arr->Get(thread_, i + 1), thread_);
77 }
78 profileTypeInfo_->SetMultiIcSlotLocked(thread_, index, JSTaggedValue::Hole(), index + 1,
79 keyForMegaIC.GetTaggedValue());
80 return;
81 }
82
83 auto factory = thread_->GetEcmaVM()->GetFactory();
84 JSHandle<TaggedArray> newArr = factory->NewTaggedArray(newLen);
85 uint32_t i = 0;
86 for (; i < arr->GetLength(); i += step) {
87 newArr->Set(thread_, i, arr->Get(thread_, i));
88 newArr->Set(thread_, i + 1, arr->Get(thread_, i + 1));
89 }
90 newArr->Set(thread_, i, GetWeakRef(hclass.GetTaggedValue()));
91 newArr->Set(thread_, i + 1, handler.GetTaggedValue());
92 profileTypeInfo_->SetMultiIcSlotLocked(thread_, index, newArr.GetTaggedValue(), index + 1, JSTaggedValue::Hole());
93 }
94
AddHandlerWithoutKey(JSHandle<JSTaggedValue> hclass,JSHandle<JSTaggedValue> handler,JSHandle<JSTaggedValue> keyForMegaIC,MegaICCache::MegaICKind kind) const95 void ProfileTypeAccessor::AddHandlerWithoutKey(JSHandle<JSTaggedValue> hclass, JSHandle<JSTaggedValue> handler,
96 JSHandle<JSTaggedValue> keyForMegaIC,
97 MegaICCache::MegaICKind kind) const
98 {
99 ALLOW_LOCAL_TO_SHARE_WEAK_REF_HANDLE;
100 auto index = slotId_;
101 if (IsNamedGlobalIC(GetKind())) {
102 profileTypeInfo_->SetIcSlot(thread_, index, handler.GetTaggedValue());
103 return;
104 }
105 auto profileData = profileTypeInfo_->GetIcSlot(thread_, slotId_);
106 ASSERT(!profileData.IsHole());
107 if (profileData.IsUndefined()) {
108 profileTypeInfo_->SetMultiIcSlotLocked(thread_, index, GetWeakRef(hclass.GetTaggedValue()),
109 index + 1, handler.GetTaggedValue());
110 return;
111 }
112 if (!profileData.IsWeak() && profileData.IsTaggedArray()) { // POLY
113 AddWithoutKeyPoly(hclass, handler, index, profileData, keyForMegaIC, kind);
114 return;
115 }
116 // MONO to POLY
117 auto factory = thread_->GetEcmaVM()->GetFactory();
118 JSHandle<TaggedArray> newArr = factory->NewTaggedArray(POLY_CASE_NUM);
119 uint32_t arrIndex = 0;
120 newArr->Set(thread_, arrIndex++, profileTypeInfo_->GetIcSlot(thread_, index));
121 newArr->Set(thread_, arrIndex++, profileTypeInfo_->GetIcSlot(thread_, index + 1));
122 newArr->Set(thread_, arrIndex++, GetWeakRef(hclass.GetTaggedValue()));
123 newArr->Set(thread_, arrIndex, handler.GetTaggedValue());
124
125 profileTypeInfo_->SetMultiIcSlotLocked(thread_, index, newArr.GetTaggedValue(), index + 1, JSTaggedValue::Hole());
126 }
127
AddHandlerWithKey(JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> hclass,JSHandle<JSTaggedValue> handler) const128 void ProfileTypeAccessor::AddHandlerWithKey(JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> hclass,
129 JSHandle<JSTaggedValue> handler) const
130 {
131 ALLOW_LOCAL_TO_SHARE_WEAK_REF_HANDLE;
132 if (IsValueGlobalIC(GetKind())) {
133 AddGlobalHandlerKey(key, handler);
134 return;
135 }
136 auto profileData = profileTypeInfo_->GetIcSlot(thread_, slotId_);
137 ASSERT(!profileData.IsHole());
138 auto index = slotId_;
139 if (profileData.IsUndefined()) {
140 const int arrayLength = 2;
141 JSHandle<TaggedArray> newArr = thread_->GetEcmaVM()->GetFactory()->NewTaggedArray(arrayLength);
142 newArr->Set(thread_, 0, GetWeakRef(hclass.GetTaggedValue()));
143 newArr->Set(thread_, 1, handler.GetTaggedValue());
144 profileTypeInfo_->SetMultiIcSlotLocked(thread_, index,
145 key.GetTaggedValue(), index + 1, newArr.GetTaggedValue());
146 return;
147 }
148 // for element ic, profileData may hclass or tagged array
149 if (key.GetTaggedValue() != profileData) {
150 profileTypeInfo_->SetMultiIcSlotLocked(thread_, index, JSTaggedValue::Hole(), index + 1, JSTaggedValue::Hole());
151 return;
152 }
153 JSTaggedValue patchValue = profileTypeInfo_->GetIcSlot(thread_, index + 1);
154 ASSERT(patchValue.IsTaggedArray());
155 JSHandle<TaggedArray> arr(thread_, patchValue);
156 const uint32_t step = 2;
157 if (arr->GetLength() > step) { // POLY
158 uint32_t newLen = arr->GetLength() + step;
159 if (newLen > CACHE_MAX_LEN) {
160 profileTypeInfo_->SetMultiIcSlotLocked(thread_, index,
161 JSTaggedValue::Hole(), index + 1, JSTaggedValue::Hole());
162 return;
163 }
164 auto factory = thread_->GetEcmaVM()->GetFactory();
165 JSHandle<TaggedArray> newArr = factory->NewTaggedArray(newLen);
166 newArr->Set(thread_, 0, GetWeakRef(hclass.GetTaggedValue()));
167 newArr->Set(thread_, 1, handler.GetTaggedValue());
168 for (uint32_t i = 0; i < arr->GetLength(); i += step) {
169 newArr->Set(thread_, i + step, arr->Get(thread_, i));
170 newArr->Set(thread_, i + step + 1, arr->Get(thread_, i + 1));
171 }
172 profileTypeInfo_->SetIcSlot(thread_, index + 1, newArr.GetTaggedValue());
173 return;
174 }
175 // MONO
176 auto factory = thread_->GetEcmaVM()->GetFactory();
177 JSHandle<TaggedArray> newArr = factory->NewTaggedArray(POLY_CASE_NUM);
178 uint32_t arrIndex = 0;
179 newArr->Set(thread_, arrIndex++, arr->Get(thread_, 0));
180 newArr->Set(thread_, arrIndex++, arr->Get(thread_, 1));
181 newArr->Set(thread_, arrIndex++, GetWeakRef(hclass.GetTaggedValue()));
182 newArr->Set(thread_, arrIndex++, handler.GetTaggedValue());
183
184 profileTypeInfo_->SetIcSlot(thread_, index + 1, newArr.GetTaggedValue());
185 }
186
AddGlobalHandlerKey(JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> handler) const187 void ProfileTypeAccessor::AddGlobalHandlerKey(JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> handler) const
188 {
189 ALLOW_LOCAL_TO_SHARE_WEAK_REF_HANDLE;
190 auto index = slotId_;
191 const uint8_t step = 2; // key and value pair
192 JSTaggedValue indexVal = profileTypeInfo_->GetIcSlot(thread_, index);
193 if (indexVal.IsUndefined()) {
194 auto factory = thread_->GetEcmaVM()->GetFactory();
195 JSHandle<TaggedArray> newArr = factory->NewTaggedArray(step);
196 newArr->Set(thread_, 0, GetWeakRef(key.GetTaggedValue()));
197 newArr->Set(thread_, 1, handler.GetTaggedValue());
198 profileTypeInfo_->SetIcSlot(thread_, index, newArr.GetTaggedValue());
199 return;
200 }
201 ASSERT(indexVal.IsTaggedArray());
202 JSHandle<TaggedArray> arr(thread_, indexVal);
203 uint32_t newLen = arr->GetLength() + step;
204 if (newLen > CACHE_MAX_LEN) {
205 profileTypeInfo_->SetIcSlot(thread_, index, JSTaggedValue::Hole());
206 return;
207 }
208 auto factory = thread_->GetEcmaVM()->GetFactory();
209 JSHandle<TaggedArray> newArr = factory->NewTaggedArray(newLen);
210 newArr->Set(thread_, 0, GetWeakRef(key.GetTaggedValue()));
211 newArr->Set(thread_, 1, handler.GetTaggedValue());
212
213 for (uint32_t i = 0; i < arr->GetLength(); i += step) {
214 newArr->Set(thread_, i + step, arr->Get(thread_, i));
215 newArr->Set(thread_, i + step + 1, arr->Get(thread_, i + 1));
216 }
217 profileTypeInfo_->SetIcSlot(thread_, index, newArr.GetTaggedValue());
218 }
219
AddGlobalRecordHandler(JSHandle<JSTaggedValue> handler) const220 void ProfileTypeAccessor::AddGlobalRecordHandler(JSHandle<JSTaggedValue> handler) const
221 {
222 uint32_t index = slotId_;
223 profileTypeInfo_->SetIcSlot(thread_, index, handler.GetTaggedValue());
224 }
SetAsMegaForTraceSlowMode(ObjectOperator & op) const225 void ProfileTypeAccessor::SetAsMegaForTraceSlowMode([[maybe_unused]] ObjectOperator& op) const
226 {
227 #if ECMASCRIPT_ENABLE_TRACE_LOAD
228 if (op.IsFoundDict()) {
229 SetAsMegaForTrace(JSTaggedValue(ProfileTypeAccessor::MegaState::DICT_MEGA));
230 } else if (!op.IsFound()) {
231 SetAsMegaForTrace(JSTaggedValue(ProfileTypeAccessor::MegaState::NOTFOUND_MEGA));
232 } else {
233 SetAsMega();
234 }
235 #else
236 SetAsMega();
237 #endif
238 }
SetAsMega() const239 void ProfileTypeAccessor::SetAsMega() const
240 {
241 if (IsGlobalIC(kind_)) {
242 profileTypeInfo_->SetIcSlot(thread_, slotId_, JSTaggedValue::Hole());
243 } else {
244 profileTypeInfo_->SetMultiIcSlotLocked(thread_, slotId_,
245 JSTaggedValue::Hole(), slotId_ + 1, JSTaggedValue::Hole());
246 }
247 }
248
SetAsMegaIfUndefined() const249 void ProfileTypeAccessor::SetAsMegaIfUndefined() const
250 {
251 if (profileTypeInfo_->GetIcSlot(thread_, slotId_).IsUndefined()) {
252 SetAsMega();
253 }
254 }
255
SetAsMegaForTrace(JSTaggedValue value) const256 void ProfileTypeAccessor::SetAsMegaForTrace(JSTaggedValue value) const
257 {
258 if (IsGlobalIC(kind_)) {
259 profileTypeInfo_->SetIcSlot(thread_, slotId_, JSTaggedValue::Hole());
260 } else {
261 profileTypeInfo_->SetMultiIcSlotLocked(thread_, slotId_,
262 JSTaggedValue::Hole(), slotId_ + 1, value);
263 }
264 }
265
ICKindToString(ICKind kind)266 std::string ICKindToString(ICKind kind)
267 {
268 switch (kind) {
269 case ICKind::NamedLoadIC:
270 return "NamedLoadIC";
271 case ICKind::NamedStoreIC:
272 return "NamedStoreIC";
273 case ICKind::LoadIC:
274 return "LoadIC";
275 case ICKind::StoreIC:
276 return "StoreIC";
277 case ICKind::NamedGlobalLoadIC:
278 return "NamedGlobalLoadIC";
279 case ICKind::NamedGlobalStoreIC:
280 return "NamedGlobalStoreIC";
281 case ICKind::NamedGlobalTryLoadIC:
282 return "NamedGlobalTryLoadIC";
283 case ICKind::NamedGlobalTryStoreIC:
284 return "NamedGlobalTryStoreIC";
285 case ICKind::GlobalLoadIC:
286 return "GlobalLoadIC";
287 case ICKind::GlobalStoreIC:
288 return "GlobalStoreIC";
289 default:
290 LOG_ECMA(FATAL) << "this branch is unreachable";
291 UNREACHABLE();
292 break;
293 }
294 }
295
ICStateToString(ProfileTypeAccessor::ICState state)296 std::string ProfileTypeAccessor::ICStateToString(ProfileTypeAccessor::ICState state)
297 {
298 switch (state) {
299 case ICState::UNINIT:
300 return "uninit";
301 case ICState::MONO:
302 return "mono";
303 case ICState::POLY:
304 return "poly";
305 case ICState::MEGA:
306 return "mega";
307 case ICState::IC_MEGA:
308 return "ic_mega";
309 default:
310 LOG_ECMA(FATAL) << "this branch is unreachable";
311 UNREACHABLE();
312 break;
313 }
314 }
GetMegaState() const315 ProfileTypeAccessor::ICState ProfileTypeAccessor::GetMegaState() const
316 {
317 if (IsGlobalIC(kind_)) {
318 return ICState::MEGA;
319 }
320 auto profileDataSecond = profileTypeInfo_->Get(thread_, slotId_ + 1);
321 if (profileDataSecond.IsString()) {
322 return ICState::IC_MEGA;
323 } else {
324 return ICState::MEGA;
325 }
326 }
327
GetICState() const328 ProfileTypeAccessor::ICState ProfileTypeAccessor::GetICState() const
329 {
330 auto profileData = profileTypeInfo_->GetIcSlot(thread_, slotId_);
331 if (profileData.IsUndefined()) {
332 return ICState::UNINIT;
333 }
334
335 if (profileData.IsHole()) {
336 return GetMegaState();
337 }
338
339 switch (kind_) {
340 case ICKind::NamedLoadIC:
341 case ICKind::NamedStoreIC:
342 if (profileData.IsWeak()) {
343 return ICState::MONO;
344 }
345 ASSERT(profileData.IsTaggedArray());
346 return ICState::POLY;
347 case ICKind::LoadIC:
348 case ICKind::StoreIC: {
349 if (profileData.IsWeak()) {
350 return ICState::MONO;
351 }
352 if (profileData.IsTaggedArray()) {
353 TaggedArray *array = TaggedArray::Cast(profileData.GetTaggedObject());
354 return array->GetLength() == MONO_CASE_NUM ? ICState::MONO : ICState::POLY; // 2 : test case
355 }
356 profileData = profileTypeInfo_->GetIcSlot(thread_, slotId_ + 1);
357 TaggedArray *array = TaggedArray::Cast(profileData.GetTaggedObject());
358 return array->GetLength() == MONO_CASE_NUM ? ICState::MONO : ICState::POLY; // 2 : test case
359 }
360 case ICKind::NamedGlobalLoadIC:
361 case ICKind::NamedGlobalStoreIC:
362 case ICKind::NamedGlobalTryLoadIC:
363 case ICKind::NamedGlobalTryStoreIC:
364 ASSERT(profileData.IsPropertyBox());
365 return ICState::MONO;
366 case ICKind::GlobalLoadIC:
367 case ICKind::GlobalStoreIC: {
368 ASSERT(profileData.IsTaggedArray());
369 TaggedArray *array = TaggedArray::Cast(profileData.GetTaggedObject());
370 return array->GetLength() == MONO_CASE_NUM ? ICState::MONO : ICState::POLY; // 2 : test case
371 }
372 default:
373 LOG_ECMA(FATAL) << "this branch is unreachable";
374 UNREACHABLE();
375 break;
376 }
377 return ICState::UNINIT;
378 }
379 } // namespace panda::ecmascript
380