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