* Copyright (c) 2021-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iomanip>
#include "pass_manager.h"
#include "compiler_logger.h"
#include "libarkbase/trace/trace.h"
#include "optimizer/ir/graph.h"
#include "optimizer/ir/graph_checker.h"
#include "optimizer/ir/graph_cloner.h"
#include "optimizer/ir/visualizer_printer.h"
#include "optimizer/analysis/alias_analysis.h"
#include "optimizer/analysis/bounds_analysis.h"
#include "optimizer/analysis/catch_inputs.h"
#include "optimizer/analysis/dominators_tree.h"
#include "optimizer/analysis/linear_order.h"
#include "optimizer/analysis/liveness_analyzer.h"
#include "optimizer/analysis/live_registers.h"
#include "optimizer/analysis/loop_analyzer.h"
#include "optimizer/analysis/monitor_analysis.h"
#include "optimizer/analysis/object_type_propagation.h"
#include "optimizer/analysis/reg_alloc_verifier.h"
#include "optimizer/analysis/rpo.h"
#include "optimizer/analysis/types_analysis.h"
#include "optimizer/optimizations/cleanup.h"
#define ENABLE_IR_DUMP
#ifdef ENABLE_IR_DUMP
#include <fstream>
#include <ctime>
#include "libarkbase/os/filesystem.h"
#endif
namespace ark::compiler {
PassManager::PassManager(Graph *graph, PassManager *parentPm)
: graph_(graph),
optimizations_(graph->GetAllocator()->Adapter()),
analyses_(details::PredefinedAnalyses::Instantiate<Analysis *>(graph_->GetAllocator(), graph_)),
stats_((parentPm == nullptr) ? graph->GetAllocator()->New<PassManagerStatistics>(graph)
: parentPm->GetStatistics())
{
}
#ifdef ENABLE_IR_DUMP
static std::string ClearFileName(std::string str, std::string_view suffix)
{
std::string delimiters = "~`@#$%^&*()-+=\\|/\"<>;,.[]";
for (const char &c : delimiters) {
std::replace(str.begin(), str.end(), c, '_');
}
return str.substr(0, NAME_MAX - suffix.size());
}
#endif
std::string PassManager::GetFileName([[maybe_unused]] const char *passName, [[maybe_unused]] const std::string &suffix)
{
#ifdef ENABLE_IR_DUMP
std::stringstream ssFilename;
std::stringstream ssFullpath;
ASSERT(GetGraph()->GetRuntime() != nullptr);
const auto &folderName(g_options.GetCompilerDumpFolder());
os::CreateDirectories(folderName);
constexpr auto IMM_3 = 3;
constexpr auto IMM_4 = 4;
ssFilename << std::setw(IMM_3) << std::setfill('0') << executionCounter_ << "_";
if (passName != nullptr) {
ssFilename << "pass_" << std::setw(IMM_4) << std::setfill('0') << stats_->GetCurrentPassIndex() << "_";
}
if (GetGraph()->GetParentGraph() != nullptr) {
ssFilename << "inlined_";
}
ssFilename << GetGraph()->GetRuntime()->GetClassNameFromMethod(GetGraph()->GetMethod()) << "_"
<< GetGraph()->GetRuntime()->GetMethodName(GetGraph()->GetMethod());
if (GetGraph()->IsOsrMode()) {
ssFilename << "_OSR";
}
if (passName != nullptr) {
ssFilename << std::string(stats_->GetCurrentPassCallDepth() + 1, '_') << passName;
}
ssFullpath << folderName.c_str() << "/" << ClearFileName(ssFilename.str(), suffix) << suffix;
return ssFullpath.str();
#else
return "";
#endif
}
static void CleanupChecksForDump(Graph *graph)
{
for (auto *block : graph->GetVectorBlocks()) {
if (block == nullptr) {
continue;
}
for (auto inst : block->InstsSafe()) {
if (inst->IsCheck()) {
inst->ReplaceUsers(Inst::GetDataFlowInput(inst));
block->RemoveInst(inst);
}
}
}
}
void PassManager::DumpGraph([[maybe_unused]] const char *passName)
{
#ifdef ENABLE_IR_DUMP
std::string fileName = GetFileName(passName, ".ir");
std::ofstream strm(fileName);
if (!strm.is_open()) {
std::cerr << errno << " ERROR: " << strerror(errno) << "\n" << fileName << std::endl;
}
ASSERT(strm.is_open());
if (g_options.IsCompilerDumpNoChecks()) {
auto clone = GraphCloner(GetGraph(), GetAllocator(), GetLocalAllocator()).CloneGraph();
ASSERT(clone != nullptr);
clone->GetPassManager()->SetCheckMode(true);
CleanupChecksForDump(clone);
clone->Dump(&strm);
} else {
GetGraph()->Dump(&strm);
}
#endif
}
void PassManager::DumpLifeIntervals([[maybe_unused]] const char *passName)
{
#ifdef ENABLE_IR_DUMP
if (!GetGraph()->IsAnalysisValid<LivenessAnalyzer>()) {
return;
}
std::ofstream strm(GetFileName(passName, ".li"));
if (!strm.is_open()) {
std::cerr << errno << " ERROR: " << strerror(errno) << "\n" << GetFileName(passName, ".li") << std::endl;
}
ASSERT(strm.is_open());
GetGraph()->GetAnalysis<LivenessAnalyzer>().DumpLifeIntervals(strm);
#endif
}
void PassManager::InitialDumpVisualizerGraph()
{
#ifdef ENABLE_IR_DUMP
std::ofstream strm(GetFileName());
strm << "begin_compilation\n";
strm << " name \"" << GetGraph()->GetRuntime()->GetClassNameFromMethod(GetGraph()->GetMethod()) << "_"
<< GetGraph()->GetRuntime()->GetMethodName(GetGraph()->GetMethod()) << "\"\n";
strm << " method \"" << GetGraph()->GetRuntime()->GetClassNameFromMethod(GetGraph()->GetMethod()) << "_"
<< GetGraph()->GetRuntime()->GetMethodName(GetGraph()->GetMethod()) << "\"\n";
strm << " date " << std::time(nullptr) << "\n";
strm << "end_compilation\n";
strm.close();
#endif
}
void PassManager::DumpVisualizerGraph([[maybe_unused]] const char *passName)
{
#ifdef ENABLE_IR_DUMP
std::ofstream strm(GetFileName(), std::ios::app);
VisualizerPrinter(GetGraph(), &strm, passName).Print();
strm.close();
#endif
}
bool PassManager::RunPass(Pass *pass, size_t localMemSizeBeforePass)
{
if (pass->IsAnalysis() && pass->IsValid()) {
return true;
}
if (!pass->IsAnalysis() && !static_cast<Optimization *>(pass)->IsEnable()) {
return false;
}
if (!IsCheckMode()) {
stats_->ProcessBeforeRun(*pass);
if (firstExecution_ && GetGraph()->GetParentGraph() == nullptr) {
StartExecution();
firstExecution_ = false;
}
}
#ifndef NDEBUG
if (g_options.IsCompilerEnableTracing()) {
trace::BeginTracePoint(pass->GetPassName());
}
#endif
bool result = pass->Run();
#ifndef NDEBUG
if (g_options.IsCompilerEnableTracing()) {
trace::EndTracePoint();
}
#endif
if (!IsCheckMode()) {
ASSERT(graph_->GetLocalAllocator()->GetAllocatedSize() >= localMemSizeBeforePass);
stats_->ProcessAfterRun(graph_->GetLocalAllocator()->GetAllocatedSize() - localMemSizeBeforePass);
}
if (pass->IsAnalysis()) {
pass->SetValid(result);
}
bool isCodegen = std::string("Codegen") == pass->GetPassName();
if (g_options.IsCompilerDump() && pass->ShouldDump() && !IsCheckMode()) {
if (!g_options.IsCompilerDumpFinal() || isCodegen) {
DumpGraph(pass->GetPassName());
}
}
if (g_options.IsCompilerVisualizerDump() && pass->ShouldDump()) {
DumpVisualizerGraph(pass->GetPassName());
}
result &= RunPassChecker(pass, result, isCodegen);
return result;
}
bool PassManager::RunPassChecker(Pass *pass, bool result, bool isCodegen)
{
if (graph_->IsAbcKit()) {
return RunPassChecker<true>(pass, result, isCodegen);
}
#ifndef NDEBUG
RunPassChecker<false>(pass, result, isCodegen);
#endif
return true;
}
template <bool FORCE_RUN>
bool PassManager::RunPassChecker(Pass *pass, bool result, bool isCodegen)
{
bool checkerEnabled = g_options.IsCompilerCheckGraph();
if (g_options.IsCompilerCheckFinal()) {
checkerEnabled = isCodegen;
}
if constexpr (FORCE_RUN) {
checkerEnabled = true;
}
if (result && !pass->IsAnalysis() && checkerEnabled) {
result &= GraphChecker(graph_, pass->GetPassName()).Check();
}
return result;
}
ArenaAllocator *PassManager::GetAllocator()
{
return graph_->GetAllocator();
}
ArenaAllocator *PassManager::GetLocalAllocator()
{
return graph_->GetLocalAllocator();
}
void PassManager::Finalize() const
{
if (g_options.IsCompilerPrintStats()) {
stats_->PrintStatistics();
}
if (g_options.WasSetCompilerDumpStatsCsv()) {
stats_->DumpStatisticsCsv();
}
}
}