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/ic_runtime.h"
17
18 #include "ecmascript/global_dictionary-inl.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/ic/ic_handler.h"
21 #include "ecmascript/ic/profile_type_info.h"
22 #include "ecmascript/interpreter/slow_runtime_stub.h"
23 #include "ecmascript/js_function.h"
24 #include "ecmascript/js_hclass-inl.h"
25 #include "ecmascript/js_proxy.h"
26 #include "ecmascript/js_tagged_value-inl.h"
27 #include "ecmascript/js_typed_array.h"
28 #include "ecmascript/object_factory-inl.h"
29 #include "ecmascript/tagged_dictionary.h"
30
31 namespace panda::ecmascript {
32 #define TRACE_IC 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
33
UpdateLoadHandler(const ObjectOperator & op,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> receiver)34 void ICRuntime::UpdateLoadHandler(const ObjectOperator &op, JSHandle<JSTaggedValue> key,
35 JSHandle<JSTaggedValue> receiver)
36 {
37 if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) {
38 return;
39 }
40 if (IsNamedIC(GetICKind())) {
41 key = JSHandle<JSTaggedValue>();
42 }
43 JSHandle<JSTaggedValue> handlerValue;
44 JSHandle<JSHClass> hclass(GetThread(), JSHandle<JSObject>::Cast(receiver)->GetClass());
45 if (op.IsElement()) {
46 if (!op.IsFound() && hclass->IsDictionaryElement()) {
47 return;
48 }
49 handlerValue = LoadHandler::LoadElement(thread_);
50 } else {
51 if (!op.IsFound()) {
52 JSTaggedValue proto = hclass->GetPrototype();
53 if (!proto.IsECMAObject()) {
54 handlerValue = LoadHandler::LoadProperty(thread_, op);
55 } else {
56 handlerValue = PrototypeHandler::LoadPrototype(thread_, op, hclass);
57 }
58 } else if (!op.IsOnPrototype()) {
59 handlerValue = LoadHandler::LoadProperty(thread_, op);
60 } else {
61 // do not support global prototype ic
62 if (IsGlobalLoadIC(GetICKind())) {
63 return;
64 }
65 handlerValue = PrototypeHandler::LoadPrototype(thread_, op, hclass);
66 }
67 }
68
69 if (key.IsEmpty()) {
70 icAccessor_.AddHandlerWithoutKey(JSHandle<JSTaggedValue>::Cast(hclass), handlerValue);
71 } else if (op.IsElement()) {
72 // do not support global element ic
73 if (IsGlobalLoadIC(GetICKind())) {
74 return;
75 }
76 icAccessor_.AddElementHandler(JSHandle<JSTaggedValue>::Cast(hclass), handlerValue);
77 } else {
78 icAccessor_.AddHandlerWithKey(key, JSHandle<JSTaggedValue>::Cast(hclass), handlerValue);
79 }
80 }
81
UpdateStoreHandler(const ObjectOperator & op,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> receiver)82 void ICRuntime::UpdateStoreHandler(const ObjectOperator &op, JSHandle<JSTaggedValue> key,
83 JSHandle<JSTaggedValue> receiver)
84 {
85 if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) {
86 return;
87 }
88 if (IsNamedIC(GetICKind())) {
89 key = JSHandle<JSTaggedValue>();
90 }
91 JSHandle<JSTaggedValue> handlerValue;
92 ASSERT(op.IsFound());
93
94 if (op.IsTSHClass()) {
95 JSHandle<JSHClass> hclass(thread_, JSHandle<JSObject>::Cast(receiver)->GetClass());
96 handlerValue = StoreTSHandler::StoreAOT(thread_, op, hclass);
97 } else if (op.IsTransition()) {
98 ASSERT(!op.IsElement());
99 if (op.IsOnPrototype()) {
100 JSHandle<JSHClass> hclass(thread_, JSHandle<JSObject>::Cast(receiver)->GetClass());
101 handlerValue = TransWithProtoHandler::StoreTransition(thread_, op, hclass);
102 } else {
103 handlerValue = TransitionHandler::StoreTransition(thread_, op);
104 }
105 } else if (op.IsOnPrototype()) {
106 // do not support global prototype ic
107 if (IsGlobalStoreIC(GetICKind())) {
108 return;
109 }
110 JSHandle<JSHClass> hclass(thread_, JSHandle<JSObject>::Cast(receiver)->GetClass());
111 handlerValue = PrototypeHandler::StorePrototype(thread_, op, hclass);
112 } else {
113 handlerValue = StoreHandler::StoreProperty(thread_, op);
114 }
115
116 if (key.IsEmpty()) {
117 icAccessor_.AddHandlerWithoutKey(receiverHClass_, handlerValue);
118 } else if (op.IsElement()) {
119 // do not support global element ic
120 if (IsGlobalStoreIC(GetICKind())) {
121 return;
122 }
123 icAccessor_.AddElementHandler(receiverHClass_, handlerValue);
124 } else {
125 icAccessor_.AddHandlerWithKey(key, receiverHClass_, handlerValue);
126 }
127 }
128
TraceIC(JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key) const129 void ICRuntime::TraceIC([[maybe_unused]] JSHandle<JSTaggedValue> receiver,
130 [[maybe_unused]] JSHandle<JSTaggedValue> key) const
131 {
132 #if TRACE_IC
133 auto kind = ICKindToString(GetICKind());
134 auto state = ProfileTypeAccessor::ICStateToString(icAccessor_.GetICState());
135 if (key->IsString()) {
136 auto keyStrHandle = JSHandle<EcmaString>::Cast(key);
137 LOG_ECMA(ERROR) << kind << " miss key is: " << EcmaStringAccessor(keyStrHandle).ToCString()
138 << ", receiver is " << receiver->GetTaggedObject()->GetClass()->IsDictionaryMode()
139 << ", state is " << state;
140 } else {
141 LOG_ECMA(ERROR) << kind << " miss " << ", state is "
142 << ", receiver is " << receiver->GetTaggedObject()->GetClass()->IsDictionaryMode()
143 << state;
144 }
145 #endif
146 }
147
LoadMiss(JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key)148 JSTaggedValue LoadICRuntime::LoadMiss(JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key)
149 {
150 if (!receiver->IsJSObject() || receiver->HasOrdinaryGet()) {
151 icAccessor_.SetAsMega();
152 return JSTaggedValue::GetProperty(thread_, receiver, key).GetValue().GetTaggedValue();
153 }
154
155 ICKind kind = GetICKind();
156 // global variable find from global record firstly
157 if (kind == ICKind::NamedGlobalLoadIC || kind == ICKind::NamedGlobalTryLoadIC) {
158 JSTaggedValue box = SlowRuntimeStub::LdGlobalRecord(thread_, key.GetTaggedValue());
159 if (!box.IsUndefined()) {
160 ASSERT(box.IsPropertyBox());
161 if (icAccessor_.GetICState() != ProfileTypeAccessor::ICState::MEGA) {
162 icAccessor_.AddGlobalRecordHandler(JSHandle<JSTaggedValue>(thread_, box));
163 }
164 return PropertyBox::Cast(box.GetTaggedObject())->GetValue();
165 }
166 }
167
168 ObjectOperator op(GetThread(), receiver, key);
169 auto result = JSHandle<JSTaggedValue>(thread_, JSObject::GetProperty(GetThread(), &op));
170 if (!op.IsFound() && kind == ICKind::NamedGlobalTryLoadIC) {
171 return SlowRuntimeStub::ThrowReferenceError(GetThread(), key.GetTaggedValue(), " is not definded");
172 }
173 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread());
174 // ic-switch
175 if (!GetThread()->GetEcmaVM()->ICEnabled()) {
176 icAccessor_.SetAsMega();
177 return result.GetTaggedValue();
178 }
179 TraceIC(receiver, key);
180 // do not cache element
181 if (!op.IsFastMode()) {
182 icAccessor_.SetAsMega();
183 return result.GetTaggedValue();
184 }
185
186 UpdateLoadHandler(op, key, receiver);
187 return result.GetTaggedValue();
188 }
189
StoreMiss(JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> value)190 JSTaggedValue StoreICRuntime::StoreMiss(JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key,
191 JSHandle<JSTaggedValue> value)
192 {
193 if (!receiver->IsJSObject() || receiver->HasOrdinaryGet()) {
194 icAccessor_.SetAsMega();
195 bool success = JSTaggedValue::SetProperty(GetThread(), receiver, key, value, true);
196 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
197 }
198
199 ICKind kind = GetICKind();
200 // global variable find from global record firstly
201 if (kind == ICKind::NamedGlobalStoreIC || kind == ICKind::NamedGlobalTryStoreIC) {
202 JSTaggedValue box = SlowRuntimeStub::LdGlobalRecord(thread_, key.GetTaggedValue());
203 if (!box.IsUndefined()) {
204 ASSERT(box.IsPropertyBox());
205 SlowRuntimeStub::TryUpdateGlobalRecord(thread_, key.GetTaggedValue(), value.GetTaggedValue());
206 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
207 if (icAccessor_.GetICState() != ProfileTypeAccessor::ICState::MEGA) {
208 icAccessor_.AddGlobalRecordHandler(JSHandle<JSTaggedValue>(thread_, box));
209 }
210 return JSTaggedValue::Undefined();
211 }
212 }
213 UpdateReceiverHClass(JSHandle<JSTaggedValue>(GetThread(), JSHandle<JSObject>::Cast(receiver)->GetClass()));
214
215 ObjectOperator op(GetThread(), receiver, key);
216 if (!op.IsFound()) {
217 if (kind == ICKind::NamedGlobalStoreIC) {
218 PropertyAttributes attr = PropertyAttributes::Default(true, true, false);
219 op.SetAttr(attr);
220 } else if (kind == ICKind::NamedGlobalTryStoreIC) {
221 return SlowRuntimeStub::ThrowReferenceError(GetThread(), key.GetTaggedValue(), " is not defined");
222 }
223 }
224 bool success = JSObject::SetProperty(&op, value, true);
225 // ic-switch
226 if (!GetThread()->GetEcmaVM()->ICEnabled()) {
227 icAccessor_.SetAsMega();
228 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
229 }
230 TraceIC(receiver, key);
231 // do not cache element
232 if (!op.IsFastMode()) {
233 icAccessor_.SetAsMega();
234 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
235 }
236 if (success) {
237 UpdateStoreHandler(op, key, receiver);
238 return JSTaggedValue::Undefined();
239 }
240 return JSTaggedValue::Exception();
241 }
242 } // namespace panda::ecmascript
243