1 /*
2 * Copyright (c) 2025 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/compiler/lazy_deopt_dependency.h"
17
18 #include "ecmascript/js_function.h"
19 #include "ecmascript/js_handle.h"
20 #include "ecmascript/js_hclass-inl.h"
21 #include "ecmascript/js_tagged_value.h"
22 #include "ecmascript/js_thread.h"
23 #include "ecmascript/stubs/runtime_stubs.h"
24
25 namespace panda::ecmascript::kungfu {
26
Register(JSHClass * hclass,DependentState state)27 void CombinedDependencies::Register(JSHClass *hclass, DependentState state)
28 {
29 // Since the value of deps_ is uint32_t, we don't need to check whether we can find it.
30 deps_[hclass] |= static_cast<uint32_t>(state);
31 }
32
Register(uint32_t detectorID,DependentState state)33 void CombinedDependencies::Register(uint32_t detectorID, DependentState state)
34 {
35 detectorDeps_[detectorID] |= static_cast<uint32_t>(state);
36 }
37
Register(DependentState state)38 void CombinedDependencies::Register(DependentState state)
39 {
40 threadDeps_ |= static_cast<uint32_t>(state);
41 }
42
InstallAll(JSThread * thread,JSHandle<JSTaggedValue> jsFunc)43 void CombinedDependencies::InstallAll(JSThread *thread, JSHandle<JSTaggedValue> jsFunc)
44 {
45 JSMutableHandle<JSHClass> hclass(thread, JSTaggedValue::Undefined());
46 for (auto iter : deps_) {
47 hclass.Update(JSTaggedValue(iter.first));
48 uint32_t collection = iter.second;
49 JSHandle<DependentInfos> dependentInfos =
50 JSHandle<DependentInfos>::Cast(JSObject::GetOrCreateDependentInfos(thread, hclass));
51 JSHandle<DependentInfos> infos = DependentInfos::AppendDependentInfos(thread,
52 jsFunc, collection, dependentInfos);
53 hclass->SetDependentInfos(thread, infos.GetTaggedValue());
54 }
55 if (threadDeps_) {
56 auto threadDependInfo = thread->GetOrCreateThreadDependentInfo();
57 JSHandle<DependentInfos> infos =
58 DependentInfos::AppendDependentInfos(thread, jsFunc, threadDeps_, threadDependInfo);
59 thread->SetDependentInfo(infos.GetTaggedValue());
60 }
61 for (auto iter : detectorDeps_) {
62 uint32_t detectorID = iter.first;
63 uint32_t collection = iter.second;
64 JSHandle<DependentInfos> dependentInfos = JSHandle<DependentInfos>::Cast(
65 JSObject::GetOrCreateDetectorDependentInfos(thread, detectorID, globalEnv_));
66 JSHandle<DependentInfos> infos = DependentInfos::AppendDependentInfos(thread,
67 jsFunc, collection, dependentInfos);
68 globalEnv_->SetDependentInfos(detectorID, JSHandle<JSTaggedValue>::Cast(infos));
69 }
70 }
71
72 // ---------------------------- LazyDeoptAllDependencies ----------------------------
73
RegisterDependency(const LazyDeoptDependency * dependency)74 void LazyDeoptAllDependencies::RegisterDependency(const LazyDeoptDependency *dependency)
75 {
76 if (dependency != nullptr) {
77 dependencies_.push_back(dependency);
78 }
79 }
80
DependOnArrayDetector(GlobalEnv * globalEnv)81 bool LazyDeoptAllDependencies::DependOnArrayDetector(GlobalEnv *globalEnv)
82 {
83 return DependOnDetector(GlobalEnv::ArrayPrototypeChangedGuardiansBits::START_BIT, globalEnv);
84 }
85
DependOnDetector(uint32_t detectorID,GlobalEnv * globalEnv)86 bool LazyDeoptAllDependencies::DependOnDetector(uint32_t detectorID, GlobalEnv *globalEnv)
87 {
88 SetGlobalEnv(globalEnv);
89 LazyDeoptDependency *dependency = new DetectorDependency(detectorID, globalEnv);
90 if (dependency->IsValid()) {
91 RegisterDependency(dependency);
92 return true;
93 }
94 delete dependency;
95 return false;
96 }
97
DependOnNotPrototype(JSHClass * hclass)98 bool LazyDeoptAllDependencies::DependOnNotPrototype(JSHClass *hclass)
99 {
100 LazyDeoptDependency *dependency = new NotPrototypeDependency(hclass);
101 if (dependency->IsValid()) {
102 RegisterDependency(dependency);
103 return true;
104 }
105 delete dependency;
106 return false;
107 }
108
109 // static
CheckStableHClass(JSHClass * hclass)110 bool LazyDeoptAllDependencies::CheckStableHClass(JSHClass *hclass)
111 {
112 return StableHClassDependency::IsValid(hclass);
113 }
114
DependOnStableHClass(JSHClass * hclass)115 bool LazyDeoptAllDependencies::DependOnStableHClass(JSHClass *hclass)
116 {
117 LazyDeoptDependency *dependency = new StableHClassDependency(hclass);
118 if (dependency->IsValid()) {
119 RegisterDependency(dependency);
120 return true;
121 }
122 delete dependency;
123 return false;
124 }
125
InitializeProtoChainForDependency(JSThread * thread,JSHClass * receiverHClass,JSHClass * & holderHClass,GlobalEnv * globalEnv,JSTaggedValue & current)126 bool LazyDeoptAllDependencies::InitializeProtoChainForDependency(JSThread *thread, JSHClass *receiverHClass,
127 JSHClass *&holderHClass, GlobalEnv *globalEnv,
128 JSTaggedValue ¤t)
129 {
130 // For "stobjbyname", the holder may not the actual holder.
131 // So when receiverHClass == holderHClass,
132 // we should check all of the chain of the receiver.
133 if (receiverHClass == holderHClass) {
134 holderHClass = nullptr;
135 }
136 if (receiverHClass->IsCompositeHClass()) {
137 if (receiverHClass->IsString()) {
138 ASSERT(globalEnv != nullptr);
139 current = globalEnv->GetStringPrototype().GetTaggedValue();
140 holderHClass = nullptr;
141 } else {
142 return false;
143 }
144 } else {
145 current = receiverHClass->GetPrototype(thread);
146 }
147 return true;
148 }
149
150 // static
CheckStableProtoChain(JSThread * thread,JSHClass * receiverHClass,JSHClass * holderHClass,GlobalEnv * globalEnv)151 bool LazyDeoptAllDependencies::CheckStableProtoChain(JSThread *thread, JSHClass *receiverHClass,
152 JSHClass *holderHClass,
153 GlobalEnv *globalEnv)
154 {
155 JSTaggedValue current;
156 if (!InitializeProtoChainForDependency(thread, receiverHClass, holderHClass, globalEnv, current)) {
157 return false;
158 }
159 bool success = true;
160 while (current.IsHeapObject()) {
161 auto currentHC = current.GetTaggedObject()->GetClass();
162 success &= CheckStableHClass(currentHC);
163 // We only need to ensure Stable of the prototype chain
164 // from the receiver's prototype to the holder.
165 if (currentHC == holderHClass) {
166 break;
167 }
168 current = currentHC->GetPrototype(thread);
169 }
170 return success;
171 }
172
DependOnStableProtoChain(JSThread * thread,JSHClass * receiverHClass,JSHClass * holderHClass,GlobalEnv * globalEnv)173 bool LazyDeoptAllDependencies::DependOnStableProtoChain(JSThread *thread, JSHClass *receiverHClass,
174 JSHClass *holderHClass,
175 GlobalEnv *globalEnv)
176 {
177 JSTaggedValue current;
178 if (!InitializeProtoChainForDependency(thread, receiverHClass, holderHClass, globalEnv, current)) {
179 return false;
180 }
181 bool success = true;
182 while (current.IsHeapObject()) {
183 auto currentHC = current.GetTaggedObject()->GetClass();
184 success &= DependOnStableHClass(currentHC);
185 // We only need to ensure Stable of the prototype chain
186 // from the receiver's prototype to the holder.
187 if (currentHC == holderHClass) {
188 break;
189 }
190 current = currentHC->GetPrototype(thread);
191 }
192 return success;
193 }
194
DependOnNotHotReloadPatchMain(JSThread * thread)195 bool LazyDeoptAllDependencies::DependOnNotHotReloadPatchMain(JSThread *thread)
196 {
197 LazyDeoptDependency *dependency = new NotHotReloadDependency(thread);
198 if (dependency->IsValid()) {
199 RegisterDependency(dependency);
200 return true;
201 }
202 delete dependency;
203 return false;
204 }
205
PreInstall(JSThread * thread)206 bool LazyDeoptAllDependencies::PreInstall(JSThread *thread)
207 {
208 for (auto dep : dependencies_) {
209 if (!dep->IsValid()) {
210 if (thread->GetEcmaVM()->GetJSOptions().IsEnableLazyDeoptTrace()) {
211 LOG_FULL(INFO) << "Lazy Deoptimized Compilation aborted due to invalid dependency";
212 }
213 return false;
214 }
215 }
216 return true;
217 }
218
219 // static
Commit(LazyDeoptAllDependencies * dependencies,JSThread * thread,JSTaggedValue jsFunc)220 bool LazyDeoptAllDependencies::Commit(LazyDeoptAllDependencies *dependencies,
221 JSThread *thread, JSTaggedValue jsFunc)
222 {
223 if (!thread->GetEcmaVM()->GetJSOptions().IsEnableJitLazyDeopt()) {
224 return true;
225 }
226 if (!dependencies->PreInstall(thread)) {
227 return false;
228 }
229 JSHandle<JSTaggedValue> jsFuncHandle(thread, jsFunc);
230 CombinedDependencies cbDependencies(dependencies->globalEnv_);
231 for (auto dep : dependencies->dependencies_) {
232 dep->Install(&cbDependencies);
233 }
234 cbDependencies.InstallAll(thread, jsFuncHandle);
235 RuntimeStubs::TraceLazyDeoptCommitSuccess(thread->GetGlueAddr(), jsFuncHandle);
236 return true;
237 }
238 } // namespace panda::ecmascript::kungfu
239