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 #ifndef ECMASCRIPT_COMPILER_PASS_H 17 #define ECMASCRIPT_COMPILER_PASS_H 18 19 #include "ecmascript/compiler/async_function_lowering.h" 20 #include "ecmascript/compiler/bytecode_circuit_builder.h" 21 #include "ecmascript/compiler/common_stubs.h" 22 #include "ecmascript/compiler/compiler_log.h" 23 #include "ecmascript/compiler/early_elimination.h" 24 #include "ecmascript/compiler/graph_linearizer.h" 25 #include "ecmascript/compiler/later_elimination.h" 26 #include "ecmascript/compiler/lcr_lowering.h" 27 #include "ecmascript/compiler/llvm_codegen.h" 28 #include "ecmascript/compiler/loop_analysis.h" 29 #include "ecmascript/compiler/loop_peeling.h" 30 #include "ecmascript/compiler/ntype_hcr_lowering.h" 31 #include "ecmascript/compiler/ntype_mcr_lowering.h" 32 #include "ecmascript/compiler/number_speculative_runner.h" 33 #include "ecmascript/compiler/scheduler.h" 34 #include "ecmascript/compiler/slowpath_lowering.h" 35 #include "ecmascript/compiler/state_split_linearizer.h" 36 #include "ecmascript/compiler/ts_class_analysis.h" 37 #include "ecmascript/compiler/ts_inline_lowering.h" 38 #include "ecmascript/compiler/ts_hcr_lowering.h" 39 #include "ecmascript/compiler/type_inference/global_type_infer.h" 40 #include "ecmascript/compiler/type_inference/initialization_analysis.h" 41 #include "ecmascript/compiler/type_inference/pgo_type_infer.h" 42 #include "ecmascript/compiler/type_mcr_lowering.h" 43 #include "ecmascript/compiler/value_numbering.h" 44 #include "ecmascript/compiler/verifier.h" 45 46 namespace panda::ecmascript::kungfu { 47 class PassContext; 48 49 class PassData { 50 public: 51 PassData(BytecodeCircuitBuilder *builder, Circuit *circuit, PassContext *ctx, CompilerLog *log, 52 std::string methodName, MethodInfo *methodInfo = nullptr, bool hasTypes = false, 53 const CString &recordName = "", MethodLiteral *methodLiteral = nullptr, 54 uint32_t methodOffset = 0, NativeAreaAllocator *allocator = nullptr, 55 PGOProfilerDecoder *decoder = nullptr, PassOptions *passOptions = nullptr) builder_(builder)56 : builder_(builder), circuit_(circuit), ctx_(ctx), log_(log), methodName_(methodName), 57 methodInfo_(methodInfo), hasTypes_(hasTypes), recordName_(recordName), methodLiteral_(methodLiteral), 58 methodOffset_(methodOffset), allocator_(allocator), decoder_(decoder), passOptions_(passOptions) 59 { 60 } 61 62 virtual ~PassData() = default; 63 GetConstScheduleResult()64 const ControlFlowGraph &GetConstScheduleResult() const 65 { 66 return cfg_; 67 } 68 GetCfg()69 ControlFlowGraph &GetCfg() 70 { 71 return cfg_; 72 } 73 GetCircuit()74 virtual Circuit* GetCircuit() const 75 { 76 return circuit_; 77 } 78 GetBuilder()79 BytecodeCircuitBuilder* GetBuilder() const 80 { 81 return builder_; 82 } 83 GetPassContext()84 PassContext* GetPassContext() const 85 { 86 return ctx_; 87 } 88 GetCompilerConfig()89 CompilationConfig* GetCompilerConfig() const 90 { 91 return ctx_->GetCompilerConfig(); 92 } 93 GetTSManager()94 TSManager* GetTSManager() const 95 { 96 return ctx_->GetTSManager(); 97 } 98 GetJSPandaFile()99 const JSPandaFile *GetJSPandaFile() const 100 { 101 return ctx_->GetJSPandaFile(); 102 } 103 GetAotModule()104 LLVMModule* GetAotModule() const 105 { 106 return ctx_->GetAOTModule(); 107 } 108 GetLog()109 CompilerLog* GetLog() const 110 { 111 return log_; 112 } 113 GetMethodName()114 const std::string& GetMethodName() const 115 { 116 return methodName_; 117 } 118 GetMethodLiteral()119 const MethodLiteral* GetMethodLiteral() const 120 { 121 return methodLiteral_; 122 } 123 GetMethodOffset()124 uint32_t GetMethodOffset() const 125 { 126 return methodOffset_; 127 } 128 GetMethodInfo()129 MethodInfo* GetMethodInfo() const 130 { 131 return methodInfo_; 132 } 133 GetMethodInfoIndex()134 size_t GetMethodInfoIndex() const 135 { 136 return methodInfo_->GetMethodInfoIndex(); 137 } 138 HasTypes()139 bool HasTypes() const 140 { 141 return hasTypes_; 142 } 143 GetRecordName()144 const CString &GetRecordName() const 145 { 146 return recordName_; 147 } 148 GetNativeAreaAllocator()149 NativeAreaAllocator* GetNativeAreaAllocator() const 150 { 151 return allocator_; 152 } 153 GetPGOProfilerDecoder()154 PGOProfilerDecoder *GetPGOProfilerDecoder() const 155 { 156 return decoder_; 157 } 158 GetPassOptions()159 PassOptions *GetPassOptions() const 160 { 161 return passOptions_; 162 } 163 IsTypeAbort()164 bool IsTypeAbort() const 165 { 166 if (hasTypes_) { 167 // A ts method which has low type percent and not marked as a resolved method 168 // should be skipped from full compilation. 169 if (methodInfo_->IsTypeInferAbort() && !methodInfo_->IsResolvedMethod()) { 170 return true; 171 } 172 } else { 173 // For js method, type infer pass will be skipped and it don't have a type percent. 174 // If we set an non zero type threshold, js method will be skipped from full compilation. 175 // The default Type threshold is -1. 176 if (ctx_->GetTSManager()->GetTypeThreshold() >= 0) { 177 return true; 178 } 179 } 180 // when a method will be full compiled, we should confirm its TypeInferAbortBit to be false 181 // maybe it used to be true in the first round of compilation. 182 methodInfo_->SetTypeInferAbort(false); 183 log_->AddCompiledMethod(methodName_, recordName_); 184 return false; 185 } 186 AbortCompilation()187 void AbortCompilation() 188 { 189 ctx_->GetBytecodeInfo().AddSkippedMethod(methodOffset_); 190 methodInfo_->SetIsCompiled(false); 191 log_->RemoveCompiledMethod(methodName_, recordName_); 192 } 193 MarkAsTypeAbort()194 void MarkAsTypeAbort() 195 { 196 methodInfo_->SetTypeInferAbort(true); 197 } 198 199 private: 200 BytecodeCircuitBuilder *builder_ {nullptr}; 201 Circuit *circuit_ {nullptr}; 202 ControlFlowGraph cfg_; 203 PassContext *ctx_ {nullptr}; 204 CompilerLog *log_ {nullptr}; 205 std::string methodName_; 206 MethodInfo *methodInfo_ {nullptr}; 207 bool hasTypes_; 208 const CString &recordName_; 209 MethodLiteral *methodLiteral_ {nullptr}; 210 uint32_t methodOffset_; 211 NativeAreaAllocator *allocator_ {nullptr}; 212 PGOProfilerDecoder *decoder_ {nullptr}; 213 PassOptions *passOptions_ {nullptr}; 214 }; 215 216 template<typename T1> 217 class PassRunner { 218 public: PassRunner(T1 * data)219 explicit PassRunner(T1* data) : data_(data) {} 220 virtual ~PassRunner() = default; 221 template<typename T2, typename... Args> RunPass(Args...args)222 bool RunPass(Args... args) 223 { 224 T2 pass; 225 return pass.Run(data_, std::forward<Args>(args)...); 226 } 227 228 private: 229 T1* data_; 230 }; 231 232 class TypeInferPass { 233 public: Run(PassData * data)234 bool Run(PassData* data) 235 { 236 PassOptions *passOptions = data->GetPassOptions(); 237 if (passOptions != nullptr && !passOptions->EnableTypeInfer()) { 238 return false; 239 } 240 TimeScope timescope("TypeInferPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 241 if (data->HasTypes()) { 242 bool enableLog = data->GetLog()->GetEnableMethodLog() && data->GetLog()->OutputType(); 243 GlobalTypeInfer globalTypeInfer(data->GetPassContext(), data->GetMethodOffset(), data->GetRecordName(), 244 data->GetPGOProfilerDecoder(), passOptions->EnableOptTrackField(), 245 enableLog); 246 globalTypeInfer.ProcessTypeInference(data->GetBuilder(), data->GetCircuit()); 247 if (data->GetMethodLiteral()->IsClassConstructor()) { 248 InitializationAnalysis initAnalysis(data->GetCircuit(), data->GetTSManager(), data->GetRecordName(), 249 data->GetMethodName(), enableLog); 250 initAnalysis.Run(); 251 } 252 } 253 return true; 254 } 255 }; 256 257 class PGOTypeInferPass { 258 public: Run(PassData * data)259 bool Run(PassData* data) 260 { 261 TimeScope timescope("PGOTypeInferPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 262 bool enableLog = data->GetLog()->GetEnableMethodLog() && data->GetLog()->OutputType(); 263 Chunk chunk(data->GetNativeAreaAllocator()); 264 PGOTypeInfer pgoTypeInfer(data->GetCircuit(), data->GetTSManager(), data->GetBuilder(), 265 data->GetMethodName(), &chunk, enableLog); 266 pgoTypeInfer.Run(); 267 return true; 268 } 269 }; 270 271 class TSClassAnalysisPass { 272 public: Run(PassData * data)273 bool Run(PassData *data) 274 { 275 TimeScope timescope("TSClassAnalysisPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 276 TSClassAnalysis analyzer(data->GetPassContext()->GetTSManager()); 277 analyzer.Run(); 278 return true; 279 } 280 }; 281 282 class TSHCRLoweringPass { 283 public: Run(PassData * data)284 bool Run(PassData* data) 285 { 286 PassOptions *passOptions = data->GetPassOptions(); 287 if (!passOptions->EnableTypeLowering()) { 288 return false; 289 } 290 TimeScope timescope("TSHCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 291 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 292 bool enableTypeLog = data->GetLog()->GetEnableMethodLog() && data->GetLog()->OutputType(); 293 TSHCRLowering lowering(data->GetCircuit(), data->GetPassContext(), 294 enableLog, enableTypeLog, data->GetMethodName()); 295 bool success = lowering.RunTSHCRLowering(); 296 if (!success) { 297 data->MarkAsTypeAbort(); 298 } 299 return true; 300 } 301 }; 302 303 class NTypeHCRLoweringPass { 304 public: Run(PassData * data)305 bool Run(PassData* data) 306 { 307 PassOptions *passOptions = data->GetPassOptions(); 308 if (!passOptions->EnableTypeLowering()) { 309 return false; 310 } 311 TimeScope timescope("NTypeHCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 312 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 313 NTypeHCRLowering lowering(data->GetCircuit(), data->GetPassContext(), 314 data->GetTSManager(), data->GetRecordName(), enableLog, data->GetMethodName()); 315 lowering.RunNTypeHCRLowering(); 316 return true; 317 } 318 }; 319 320 class TypeMCRLoweringPass { 321 public: Run(PassData * data)322 bool Run(PassData* data) 323 { 324 PassOptions *passOptions = data->GetPassOptions(); 325 if (!passOptions->EnableTypeLowering()) { 326 return false; 327 } 328 TimeScope timescope("TypeMCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 329 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 330 TypeMCRLowering lowering(data->GetCircuit(), data->GetCompilerConfig(), data->GetTSManager(), 331 enableLog, data->GetMethodName()); 332 lowering.RunTypeMCRLowering(); 333 return true; 334 } 335 }; 336 337 class NTypeMCRLoweringPass { 338 public: Run(PassData * data)339 bool Run(PassData* data) 340 { 341 PassOptions *passOptions = data->GetPassOptions(); 342 if (!passOptions->EnableTypeLowering()) { 343 return false; 344 } 345 TimeScope timescope("NTypeMCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 346 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 347 NTypeMCRLowering lowering(data->GetCircuit(), data->GetPassContext(), 348 data->GetRecordName(), enableLog, data->GetMethodName()); 349 lowering.RunNTypeMCRLowering(); 350 return true; 351 } 352 }; 353 354 class LCRLoweringPass { 355 public: Run(PassData * data)356 bool Run(PassData* data) 357 { 358 PassOptions *passOptions = data->GetPassOptions(); 359 if (!passOptions->EnableTypeLowering()) { 360 return false; 361 } 362 TimeScope timescope("LCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 363 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 364 LCRLowering lowering(data->GetCircuit(), data->GetCompilerConfig(), enableLog, data->GetMethodName()); 365 lowering.Run(); 366 return true; 367 } 368 }; 369 370 class TSInlineLoweringPass { 371 public: Run(PassData * data)372 bool Run(PassData* data) 373 { 374 PassOptions *passOptions = data->GetPassOptions(); 375 if (!passOptions->EnableOptInlining() || !passOptions->EnableTypeLowering()) { 376 return false; 377 } 378 TimeScope timescope("TSInlineLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 379 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 380 TSInlineLowering inlining(data->GetCircuit(), data->GetPassContext(), enableLog, data->GetMethodName(), 381 data->GetNativeAreaAllocator(), passOptions, data->GetMethodOffset()); 382 inlining.RunTSInlineLowering(); 383 return true; 384 } 385 }; 386 387 class SlowPathLoweringPass { 388 public: Run(PassData * data)389 bool Run(PassData* data) 390 { 391 TimeScope timescope("SlowPathLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 392 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 393 SlowPathLowering lowering(data->GetCircuit(), data->GetCompilerConfig(), data->GetTSManager(), 394 data->GetMethodLiteral(), enableLog, data->GetMethodName()); 395 lowering.CallRuntimeLowering(); 396 return true; 397 } 398 }; 399 400 class VerifierPass { 401 public: Run(PassData * data)402 bool Run(PassData* data) 403 { 404 TimeScope timescope("VerifierPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 405 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 406 bool isQualified = Verifier::Run(data->GetCircuit(), data->GetMethodName(), enableLog); 407 if (!isQualified) { 408 LOG_FULL(FATAL) << "VerifierPass fail"; 409 UNREACHABLE(); 410 } 411 return isQualified; 412 } 413 }; 414 415 class NumberSpeculativePass { 416 public: Run(PassData * data)417 bool Run(PassData* data) 418 { 419 PassOptions *passOptions = data->GetPassOptions(); 420 if (!passOptions->EnableTypeLowering()) { 421 return false; 422 } 423 TimeScope timescope("NumberSpeculativePass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 424 Chunk chunk(data->GetNativeAreaAllocator()); 425 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 426 NumberSpeculativeRunner(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk).Run(); 427 return true; 428 } 429 }; 430 431 class LoopOptimizationPass { 432 public: Run(PassData * data)433 bool Run(PassData* data) 434 { 435 TimeScope timescope("LoopOptimizationPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 436 Chunk chunk(data->GetNativeAreaAllocator()); 437 const auto& headList = data->GetBuilder()->GetLoopHeads(); 438 LoopAnalysis loopAnalysis_(data->GetCircuit(), &chunk); 439 for (auto head : headList) { 440 auto bb = data->GetBuilder()->GetBasicBlockById(head.second); 441 auto loopInfo = new LoopInfo(&chunk, bb.stateCurrent); 442 loopAnalysis_.CollectLoopBody(loopInfo); 443 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 444 if (enableLog) { 445 loopAnalysis_.PrintLoop(loopInfo); 446 } 447 if (data->GetPassOptions()->EnableOptLoopPeeling()) { 448 LoopPeeling(data->GetBuilder(), data->GetCircuit(), enableLog, 449 data->GetMethodName(), &chunk, loopInfo).Peel(); 450 } 451 } 452 loopAnalysis_.LoopExitElimination(); 453 return true; 454 } 455 }; 456 457 class EarlyEliminationPass { 458 public: Run(PassData * data)459 bool Run(PassData* data) 460 { 461 PassOptions *passOptions = data->GetPassOptions(); 462 if (!passOptions->EnableTypeLowering() || !passOptions->EnableEarlyElimination()) { 463 return false; 464 } 465 TimeScope timescope("EarlyEliminationPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 466 Chunk chunk(data->GetNativeAreaAllocator()); 467 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 468 EarlyElimination(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk).Run(); 469 return true; 470 } 471 }; 472 473 class LaterEliminationPass { 474 public: Run(PassData * data)475 bool Run(PassData* data) 476 { 477 PassOptions *passOptions = data->GetPassOptions(); 478 if (!passOptions->EnableTypeLowering() || !passOptions->EnableLaterElimination()) { 479 return false; 480 } 481 TimeScope timescope("LaterEliminationPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 482 Chunk chunk(data->GetNativeAreaAllocator()); 483 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 484 LaterElimination(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk).Run(); 485 return true; 486 } 487 }; 488 489 class ValueNumberingPass { 490 public: Run(PassData * data)491 bool Run(PassData* data) 492 { 493 PassOptions *passOptions = data->GetPassOptions(); 494 if (!passOptions->EnableTypeLowering() || !passOptions->EnableValueNumbering()) { 495 return false; 496 } 497 TimeScope timescope("ValueNumberingPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 498 Chunk chunk(data->GetNativeAreaAllocator()); 499 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 500 ValueNumbering(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk).Run(); 501 return true; 502 } 503 }; 504 505 class SchedulingPass { 506 public: Run(PassData * data)507 bool Run(PassData* data) 508 { 509 TimeScope timescope("SchedulingPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 510 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 511 Scheduler::Run(data->GetCircuit(), data->GetCfg(), data->GetMethodName(), enableLog); 512 return true; 513 } 514 }; 515 516 class StateSplitLinearizerPass { 517 public: Run(PassData * data)518 bool Run(PassData* data) 519 { 520 PassOptions *passOptions = data->GetPassOptions(); 521 if (!passOptions->EnableTypeLowering()) { 522 return false; 523 } 524 TimeScope timescope("StateSplitLinearizerPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 525 Chunk chunk(data->GetNativeAreaAllocator()); 526 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 527 StateSplitLinearizer(data->GetCircuit(), data->GetCompilerConfig(), 528 enableLog, data->GetMethodName(), &chunk).Run(); 529 return true; 530 } 531 }; 532 533 class GraphLinearizerPass { 534 public: Run(PassData * data)535 bool Run(PassData* data) 536 { 537 TimeScope timescope("GraphLinearizerPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 538 Chunk chunk(data->GetNativeAreaAllocator()); 539 bool enableLog = data->GetLog()->EnableMethodCIRLog(); 540 GraphLinearizer(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk).Run(data->GetCfg()); 541 return true; 542 } 543 }; 544 545 class LLVMIRGenPass { 546 public: CreateCodeGen(LLVMModule * module,bool enableLog)547 void CreateCodeGen(LLVMModule *module, bool enableLog) 548 { 549 llvmImpl_ = std::make_unique<LLVMIRGeneratorImpl>(module, enableLog); 550 } 551 Run(PassData * data)552 bool Run(PassData *data) 553 { 554 auto module = data->GetAotModule(); 555 TimeScope timescope("LLVMIRGenPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); 556 bool enableLog = data->GetLog()->EnableMethodCIRLog() || data->GetLog()->OutputASM(); 557 CreateCodeGen(module, enableLog); 558 CodeGenerator codegen(llvmImpl_, data->GetMethodName()); 559 codegen.Run(data->GetCircuit(), data->GetConstScheduleResult(), data->GetCompilerConfig(), 560 data->GetMethodLiteral(), data->GetJSPandaFile()); 561 return true; 562 } 563 private: 564 std::unique_ptr<CodeGeneratorImpl> llvmImpl_ {nullptr}; 565 }; 566 567 class AsyncFunctionLoweringPass { 568 public: Run(PassData * data)569 bool Run(PassData* data) 570 { 571 TimeScope timescope("AsyncFunctionLoweringPass", data->GetMethodName(), 572 data->GetMethodOffset(), data->GetLog()); 573 bool enableLog = data->GetLog()->EnableMethodCIRLog() || data->GetLog()->OutputASM(); 574 AsyncFunctionLowering lowering(data->GetBuilder(), data->GetCircuit(), data->GetCompilerConfig(), 575 enableLog, data->GetMethodName()); 576 if (lowering.IsAsyncRelated()) { 577 lowering.ProcessAll(); 578 } 579 return true; 580 } 581 }; 582 } // namespace panda::ecmascript::kungfu 583 #endif 584