• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &current)
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