• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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/jit/compile_decision.h"
17 #include "ecmascript/jspandafile/js_pandafile.h"
18 #include "ecmascript/ic/profile_type_info.h"
19 #include "ecmascript/platform/aot_crash_info.h"
20 
21 namespace panda::ecmascript {
GetFileName(const std::string & fileName,bool isApp)22 std::string GetFileName(const std::string& fileName, bool isApp)
23 {
24     if (isApp) {
25         std::string pathOnMobile;
26         std::string sanboxPath = panda::os::file::File::GetExtendedFilePath(AotCrashInfo::GetSandBoxPath());
27         if (ecmascript::RealPath(sanboxPath, pathOnMobile, false)) {
28             return pathOnMobile + "/method_compiled_by_jit.cfg";
29         } else {
30             LOG_JIT(ERROR) << "get method_compiled_by_jit.cfg path fail: " << sanboxPath;
31             return "";
32         }
33     }
34     return fileName;
35 }
36 
Init(EcmaVM * vm)37 void MethodNameCollector::Init(EcmaVM *vm)
38 {
39     if (vm == nullptr || isInit_) {
40         return;
41     }
42     if (!(vm->GetJSOptions().IsEnableJitMethodCollect())) {
43         return;
44     }
45     enable_ = true;
46     std::string fileName = vm->GetJSOptions().GetJitMethodPath();
47     fileName = GetFileName(fileName, vm->GetJSOptions().IsEnableAPPJIT());
48     if (fileName.empty()) {
49         return;
50     }
51     file_.open(fileName.c_str(), std::ofstream::out | std::ofstream::trunc);
52     if (!file_.is_open()) {
53         LOG_JIT(ERROR) << "open method_compiled_by_jit.cfg fail: " << fileName;
54         return;
55     }
56     LOG_JIT(INFO) << "open method_compiled_by_jit.cfg succ: " << fileName;
57     isInit_ = true;
58 }
59 
Collect(const std::string & methodFullName) const60 void MethodNameCollector::Collect(const std::string& methodFullName) const
61 {
62     if (enable_ && isInit_) {
63         file_ << methodFullName << std::endl;
64     }
65 }
66 
~MethodNameCollector()67 MethodNameCollector::~MethodNameCollector()
68 {
69     if (enable_ && isInit_) {
70         ASSERT(file_.is_open());
71         file_.close();
72     }
73     isInit_ = false;
74     enable_ = false;
75 }
76 
Init(EcmaVM * vm)77 void MethodNameFilter::Init(EcmaVM *vm)
78 {
79     if (vm == nullptr || isInit_) {
80         return;
81     }
82     if (!(vm->GetJSOptions().IsEnableJitMethodFilter())) {
83         return;
84     }
85     enable_ = true;
86     std::string fileName = vm->GetJSOptions().GetJitMethodPath();
87     fileName = GetFileName(fileName, vm->GetJSOptions().IsEnableAPPJIT());
88     if (fileName.empty()) {
89         return;
90     }
91     std::ifstream file(fileName.c_str());
92     if (!file.is_open()) {
93         LOG_JIT(INFO) << "open method_compiled_by_jit.cfg fail: " << fileName;
94         return;
95     }
96     LOG_JIT(INFO) << "open method_compiled_by_jit.cfg succ: " << fileName;
97     std::string methodFullName;
98     while (getline(file, methodFullName)) {
99         if (methodFullName.empty()) {
100             continue;
101         }
102         methodFullNames.insert(methodFullName);
103     }
104     if (methodFullNames.empty()) {
105         LOG_JIT(INFO) << "the number of method names is 0.";
106         return;
107     }
108     isInit_ = true;
109 }
110 
NeedCompiledByJit(const std::string & methodFullName) const111 bool MethodNameFilter::NeedCompiledByJit(const std::string& methodFullName) const
112 {
113     if (enable_ && isInit_) {
114         return methodFullNames.find(methodFullName) != methodFullNames.end();
115     }
116     // When filtering is not enabled, all JS functions need to be compiled.
117     return true;
118 }
119 
~MethodNameFilter()120 MethodNameFilter::~MethodNameFilter()
121 {
122     methodFullNames.clear();
123     enable_ = false;
124     isInit_ = false;
125 }
126 
127 MethodNameCollector CompileDecision::methodNameCollector;
128 MethodNameFilter CompileDecision::methodNameFilter;
129 
CompileDecision(EcmaVM * vm,JSHandle<JSFunction> & jsFunction,CompilerTier tier,int32_t osrOffset,JitCompileMode mode)130 CompileDecision::CompileDecision(EcmaVM *vm, JSHandle<JSFunction> &jsFunction, CompilerTier tier,
131     int32_t osrOffset, JitCompileMode mode) : vm_(vm), jsFunction_(jsFunction),
132     tier_(tier), osrOffset_(osrOffset), compileMode_(mode) { }
133 
GetMethodInfo() const134 CString CompileDecision::GetMethodInfo() const
135 {
136     uint32_t codeSize = GetCodeSize();
137     return GetMethodName() + ", bytecode size:" + ToCString(codeSize);
138 }
139 
GetMethodName() const140 CString CompileDecision::GetMethodName() const
141 {
142     JSThread *thread = vm_->GetJSThread();
143     Method *method = Method::Cast(jsFunction_->GetMethod(thread).GetTaggedObject());
144     ASSERT(method != nullptr);
145     auto jSPandaFile = method->GetJSPandaFile(thread);
146     CString fileDesc;
147     if (jSPandaFile != nullptr) {
148         fileDesc = jSPandaFile->GetJSPandaFileDesc();
149     }
150     return fileDesc + ":" + method->GetRecordNameStr(thread) + "." + CString(method->GetMethodName(thread));
151 }
152 
GetCodeSize() const153 uint32_t CompileDecision::GetCodeSize() const
154 {
155     JSThread *thread = vm_->GetJSThread();
156     Method *method = Method::Cast(jsFunction_->GetMethod(thread).GetTaggedObject());
157     return method->GetCodeSize(thread);
158 }
159 
Decision()160 bool CompileDecision::Decision()
161 {
162     return IsGoodCompilationRequest();
163 }
164 
IsGoodCompilationRequest() const165 bool CompileDecision::IsGoodCompilationRequest() const
166 {
167     if (!CheckJsFunctionStatus()) {
168         return false;
169     }
170 
171     if (!IsJsFunctionSupportCompile()) {
172         DisableJitCompile();
173         return false;
174     }
175 
176     if (!CheckVmState()) {
177         return false;
178     }
179     return true;
180 }
181 
IsJsFunctionSupportCompile() const182 bool CompileDecision::IsJsFunctionSupportCompile() const
183 {
184     if (!IsSupportFunctionKind()) {
185         return false;
186     }
187 
188     uint32_t maxSize = 9000;
189     if (vm_->GetJSOptions().IsEnableJitFastCompile()) {
190         maxSize = 15; // 15 is method codesize threshold during fast compiling
191     }
192     if (GetCodeSize() > maxSize && !(vm_->GetJSOptions().IsEnableForceJitCompileMain() && compileMode_.IsSync())) {
193         LOG_JIT(DEBUG) << tier_ << "skip jit task, as too large:" << GetMethodInfo();
194         return false;
195     }
196     Method *method = Method::Cast(jsFunction_->GetMethod(vm_->GetJSThread()).GetTaggedObject());
197     if (vm_->IsEnableOsr() && osrOffset_ != MachineCode::INVALID_OSR_OFFSET &&
198         method->HasCatchBlock(vm_->GetJSThread())) {
199         LOG_JIT(DEBUG) << "skip jit task, as osr does not support catch blocks: " << GetMethodInfo();
200         return false;
201     }
202     methodNameCollector.Collect(std::string(GetMethodName()));
203     if (!methodNameFilter.NeedCompiledByJit(std::string(GetMethodName()))) {
204         LOG_JIT(DEBUG) << "skip jit task, as not the compilation target:" << GetMethodInfo();
205         return false;
206     }
207     return true;
208 }
209 
IsSupportFunctionKind() const210 bool CompileDecision::IsSupportFunctionKind() const
211 {
212     Method *method = Method::Cast(jsFunction_->GetMethod(vm_->GetJSThread()).GetTaggedObject());
213     if (jsFunction_.GetTaggedValue().IsJSSharedFunction()) {
214         LOG_JIT(DEBUG) << tier_ << "method does not support compile shared function:" << GetMethodInfo();
215         return false;
216     }
217 
218     FunctionKind kind = method->GetFunctionKind();
219     switch (kind) {
220         case FunctionKind::NORMAL_FUNCTION:
221         case FunctionKind::GETTER_FUNCTION:
222         case FunctionKind::SETTER_FUNCTION:
223         case FunctionKind::ARROW_FUNCTION:
224         case FunctionKind::BASE_CONSTRUCTOR:
225         case FunctionKind::CLASS_CONSTRUCTOR:
226         case FunctionKind::DERIVED_CONSTRUCTOR:
227         case FunctionKind::NONE_FUNCTION:
228             return true;
229         default:
230             break;
231     }
232     LOG_JIT(DEBUG) << tier_ << "method does not support jit:" << GetMethodInfo() << ", kind:" << static_cast<int>(kind);
233     return false;
234 }
235 
CheckJsFunctionStatus() const236 bool CompileDecision::CheckJsFunctionStatus() const
237 {
238     if (tier_.IsFast() && jsFunction_->IsJitCompiling()) {
239         return false;
240     }
241 
242     if (tier_.IsBaseLine() && jsFunction_->IsBaselinejitCompiling()) {
243         return false;
244     }
245 
246     if (tier_.IsFast() && jsFunction_->IsCompiledCode()) {
247         JSTaggedValue machineCode = jsFunction_->GetMachineCode(vm_->GetJSThread());
248         if (machineCode.IsMachineCodeObject() &&
249             MachineCode::Cast(machineCode.GetTaggedObject())->GetOSROffset() == MachineCode::INVALID_OSR_OFFSET) {
250             return false;
251         }
252         return true;
253     }
254 
255     if (tier_.IsBaseLine() && !jsFunction_->GetBaselineCode(vm_->GetJSThread()).IsUndefined()) {
256         return false;
257     }
258     return true;
259 }
260 
DisableJitCompile() const261 void CompileDecision::DisableJitCompile() const
262 {
263     jsFunction_->SetJitHotnessCnt(vm_->GetJSThread(), ProfileTypeInfo::JIT_DISABLE_FLAG);
264 }
265 
CheckVmState() const266 bool CompileDecision::CheckVmState() const
267 {
268     if (vm_->GetJSThread()->IsMachineCodeLowMemory()) {
269         LOG_JIT(DEBUG) << tier_ << "skip jit task, as low code memory:" << GetMethodInfo();
270         return false;
271     }
272     return true;
273 }
274 }  // namespace panda::ecmascript
275