1/**
2 * Copyright (c) 2021-2022 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 "trace/trace.h"
17#include "pass_manager.h"
18#include "optimizer/ir/graph_checker.h"
19#include "optimizer/analysis/dominators_tree.h"
20#include "optimizer/analysis/linear_order.h"
21#include "optimizer/analysis/liveness_analyzer.h"
22#include "optimizer/analysis/loop_analyzer.h"
23#include "optimizer/analysis/rpo.h"
24#include "optimizer/optimizations/cleanup.h"
25
26// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
27#define ENABLE_IR_DUMP
28
29namespace panda::compiler {
30PassManager::PassManager(Graph *graph, PassManager *parent_pm)
31    : graph_(graph),
32      optimizations_(graph->GetAllocator()->Adapter()),
33      ANALYSES(details::PredefinedAnalyses::Instantiate<Analysis *>(graph_->GetAllocator(), graph_)),
34      stats_((parent_pm == nullptr) ? graph->GetAllocator()->New<PassManagerStatistics>(graph)
35                                    : parent_pm->GetStatistics())
36{
37}
38
39#if defined(ENABLE_IR_DUMP) && !defined(PANDA_TARGET_MACOS)
40static std::string ClearFileName(std::string str, std::string_view suffix)
41{
42    std::string delimiters = "~`@#$%^&*()-+=\\|/\"<>;,.[]";
43    for (const char &c : delimiters) {
44        std::replace(str.begin(), str.end(), c, '_');
45    }
46    return str.substr(0, NAME_MAX - suffix.size());
47}
48#endif  // ENABLE_IR_DUMP && !PANDA_TARGET_MACOS
49
50std::string PassManager::GetFileName([[maybe_unused]] const char *pass_name, [[maybe_unused]] const std::string &suffix)
51{
52#if defined(ENABLE_IR_DUMP) && !defined(PANDA_TARGET_MACOS)
53    std::stringstream ss_filename;
54    std::stringstream ss_fullpath;
55    ASSERT(GetGraph()->GetRuntime() != nullptr);
56
57    std::string folder_name(options.GetCompilerDumpFolder());
58
59    os::CreateDirectories(folder_name);
60    constexpr auto IMM_3 = 3;
61    constexpr auto IMM_4 = 4;
62    ss_filename << std::setw(IMM_3) << std::setfill('0') << execution_counter << "_";
63    if (pass_name != nullptr) {
64        ss_filename << "pass_" << std::setw(IMM_4) << std::setfill('0') << stats_->GetCurrentPassIndex() << "_";
65    }
66    if (GetGraph()->GetParentGraph() != nullptr) {
67        ss_filename << "inlined_";
68    }
69    ss_filename << GetGraph()->GetRuntime()->GetClassNameFromMethod(GetGraph()->GetMethod()) << "_"
70                << GetGraph()->GetRuntime()->GetMethodName(GetGraph()->GetMethod());
71    if (GetGraph()->IsOsrMode()) {
72        ss_filename << "_OSR";
73    }
74    if (pass_name != nullptr) {
75        ss_filename << "_" << pass_name;
76    }
77    ss_fullpath << folder_name.c_str() << "/" << ClearFileName(ss_filename.str(), suffix) << suffix;
78    return ss_fullpath.str();
79#else
80    return "";
81#endif  // ENABLE_IR_DUMP && !PANDA_TARGET_MACOS
82}
83void PassManager::DumpGraph([[maybe_unused]] const char *pass_name)
84{
85#if defined(ENABLE_IR_DUMP) && !defined(PANDA_TARGET_MACOS)
86    std::ofstream strm(GetFileName(pass_name, ".ir"));
87    if (!strm.is_open()) {
88        std::cerr << errno << " ERROR: " << strerror(errno) << "\n" << GetFileName(pass_name, ".ir") << std::endl;
89    }
90    ASSERT(strm.is_open());
91    GetGraph()->Dump(&strm);
92#endif  // ENABLE_IR_DUMP && !PANDA_TARGET_MACOS
93}
94void PassManager::DumpLifeIntervals([[maybe_unused]] const char *pass_name)
95{
96#if defined(ENABLE_IR_DUMP) && !defined(PANDA_TARGET_MACOS)
97    if (!GetGraph()->IsAnalysisValid<LivenessAnalyzer>()) {
98        return;
99    }
100    std::ofstream strm(GetFileName(pass_name, ".li"));
101    if (!strm.is_open()) {
102        std::cerr << errno << " ERROR: " << strerror(errno) << "\n" << GetFileName(pass_name, ".li") << std::endl;
103    }
104
105    ASSERT(strm.is_open());
106    GetGraph()->GetAnalysis<LivenessAnalyzer>().DumpLifeIntervals(strm);
107#endif  // ENABLE_IR_DUMP && !PANDA_TARGET_MACOS
108}
109
110bool PassManager::RunPass(Pass *pass, size_t local_mem_size_before_pass)
111{
112    if (pass->IsAnalysis() && pass->IsValid()) {
113        return true;
114    }
115
116    if (!pass->IsAnalysis() && !static_cast<Optimization *>(pass)->IsEnable()) {
117        return false;
118    }
119
120    if (!IsCheckMode()) {
121        stats_->ProcessBeforeRun(*pass);
122        if (first_execution_ && GetGraph()->GetParentGraph() == nullptr) {
123            StartExecution();
124            first_execution_ = false;
125        }
126    }
127
128#ifndef NDEBUG
129    if (options.IsCompilerEnableTracing()) {
130        trace::BeginTracePoint(pass->GetPassName());
131    }
132#endif  // NDEBUG
133
134    bool result = pass->Run();
135
136#ifndef NDEBUG
137    if (options.IsCompilerEnableTracing()) {
138        trace::EndTracePoint();
139    }
140#endif  // NDEBUG
141
142    if (!IsCheckMode()) {
143        ASSERT(graph_->GetLocalAllocator()->GetAllocatedSize() >= local_mem_size_before_pass);
144        stats_->ProcessAfterRun(graph_->GetLocalAllocator()->GetAllocatedSize() - local_mem_size_before_pass);
145    }
146
147    if (pass->IsAnalysis()) {
148        pass->SetValid(result);
149    }
150    if (options.IsCompilerDump() && pass->ShouldDump() && !IsCheckMode()) {
151        if (!options.IsCompilerDumpFinal()) {
152            DumpGraph(pass->GetPassName());
153        }
154    }
155
156#ifndef NDEBUG
157    bool checker_enabled = options.IsCompilerCheckGraph();
158    if (options.IsCompilerCheckFinal()) {
159        checker_enabled = false;
160    }
161    if (result && !pass->IsAnalysis() && checker_enabled) {
162        GraphChecker(graph_).Check();
163    }
164#endif
165    return result;
166}
167
168ArenaAllocator *PassManager::GetAllocator()
169{
170    return graph_->GetAllocator();
171}
172
173ArenaAllocator *PassManager::GetLocalAllocator()
174{
175    return graph_->GetLocalAllocator();
176}
177
178void PassManager::Finalize() const
179{
180    if (options.IsCompilerPrintStats()) {
181        stats_->PrintStatistics();
182    }
183    if (options.WasSetCompilerDumpStatsCsv()) {
184        stats_->DumpStatisticsCsv();
185    }
186}
187}  // namespace panda::compiler
188