1 /*
2 * Copyright (c) 2021-2025 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 <iomanip>
17 #include "pass_manager.h"
18 #include "compiler_logger.h"
19 #include "trace/trace.h"
20
21 #include "optimizer/ir/graph.h"
22 #include "optimizer/ir/graph_checker.h"
23 #include "optimizer/ir/graph_cloner.h"
24 #include "optimizer/ir/visualizer_printer.h"
25
26 #include "optimizer/analysis/alias_analysis.h"
27 #include "optimizer/analysis/bounds_analysis.h"
28 #include "optimizer/analysis/catch_inputs.h"
29 #include "optimizer/analysis/dominators_tree.h"
30 #include "optimizer/analysis/linear_order.h"
31 #include "optimizer/analysis/liveness_analyzer.h"
32 #include "optimizer/analysis/live_registers.h"
33 #include "optimizer/analysis/loop_analyzer.h"
34 #include "optimizer/analysis/monitor_analysis.h"
35 #include "optimizer/analysis/object_type_propagation.h"
36 #include "optimizer/analysis/reg_alloc_verifier.h"
37 #include "optimizer/analysis/rpo.h"
38 #include "optimizer/analysis/types_analysis.h"
39 #include "optimizer/optimizations/cleanup.h"
40
41 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
42 #define ENABLE_IR_DUMP
43
44 #ifdef ENABLE_IR_DUMP
45
46 #include <fstream>
47 #include <ctime>
48 #include "os/filesystem.h"
49 #endif // ENABLE_IR_DUMP
50
51 namespace ark::compiler {
PassManager(Graph * graph,PassManager * parentPm)52 PassManager::PassManager(Graph *graph, PassManager *parentPm)
53 : graph_(graph),
54 optimizations_(graph->GetAllocator()->Adapter()),
55 analyses_(details::PredefinedAnalyses::Instantiate<Analysis *>(graph_->GetAllocator(), graph_)),
56 stats_((parentPm == nullptr) ? graph->GetAllocator()->New<PassManagerStatistics>(graph)
57 : parentPm->GetStatistics())
58 {
59 }
60
61 #ifdef ENABLE_IR_DUMP
ClearFileName(std::string str,std::string_view suffix)62 static std::string ClearFileName(std::string str, std::string_view suffix)
63 {
64 std::string delimiters = "~`@#$%^&*()-+=\\|/\"<>;,.[]";
65 for (const char &c : delimiters) {
66 std::replace(str.begin(), str.end(), c, '_');
67 }
68 return str.substr(0, NAME_MAX - suffix.size());
69 }
70 #endif
71
GetFileName(const char * passName,const std::string & suffix)72 std::string PassManager::GetFileName([[maybe_unused]] const char *passName, [[maybe_unused]] const std::string &suffix)
73 {
74 #ifdef ENABLE_IR_DUMP
75 std::stringstream ssFilename;
76 std::stringstream ssFullpath;
77 ASSERT(GetGraph()->GetRuntime() != nullptr);
78
79 const auto &folderName(g_options.GetCompilerDumpFolder());
80
81 os::CreateDirectories(folderName);
82 constexpr auto IMM_3 = 3;
83 constexpr auto IMM_4 = 4;
84 ssFilename << std::setw(IMM_3) << std::setfill('0') << executionCounter_ << "_";
85 if (passName != nullptr) {
86 ssFilename << "pass_" << std::setw(IMM_4) << std::setfill('0') << stats_->GetCurrentPassIndex() << "_";
87 }
88 if (GetGraph()->GetParentGraph() != nullptr) {
89 ssFilename << "inlined_";
90 }
91 ssFilename << GetGraph()->GetRuntime()->GetClassNameFromMethod(GetGraph()->GetMethod()) << "_"
92 << GetGraph()->GetRuntime()->GetMethodName(GetGraph()->GetMethod());
93 if (GetGraph()->IsOsrMode()) {
94 ssFilename << "_OSR";
95 }
96 if (passName != nullptr) {
97 ssFilename << "_" << passName;
98 }
99 ssFullpath << folderName.c_str() << "/" << ClearFileName(ssFilename.str(), suffix) << suffix;
100 return ssFullpath.str();
101 #else
102 return "";
103 #endif // ENABLE_IR_DUMP
104 }
105
CleanupChecksForDump(Graph * graph)106 static void CleanupChecksForDump(Graph *graph)
107 {
108 for (auto *block : graph->GetVectorBlocks()) {
109 if (block == nullptr) {
110 continue;
111 }
112 for (auto inst : block->InstsSafe()) {
113 if (inst->IsCheck()) {
114 inst->ReplaceUsers(Inst::GetDataFlowInput(inst));
115 block->RemoveInst(inst);
116 }
117 }
118 }
119 }
120
DumpGraph(const char * passName)121 void PassManager::DumpGraph([[maybe_unused]] const char *passName)
122 {
123 #ifdef ENABLE_IR_DUMP
124 std::string fileName = GetFileName(passName, ".ir");
125 std::ofstream strm(fileName);
126 if (!strm.is_open()) {
127 std::cerr << errno << " ERROR: " << strerror(errno) << "\n" << fileName << std::endl;
128 }
129 ASSERT(strm.is_open());
130 if (g_options.IsCompilerDumpNoChecks()) {
131 auto clone = GraphCloner(GetGraph(), GetAllocator(), GetLocalAllocator()).CloneGraph();
132 ASSERT(clone != nullptr);
133 clone->GetPassManager()->SetCheckMode(true);
134 CleanupChecksForDump(clone);
135 clone->Dump(&strm);
136 } else {
137 GetGraph()->Dump(&strm);
138 }
139 #endif // ENABLE_IR_DUMP
140 }
DumpLifeIntervals(const char * passName)141 void PassManager::DumpLifeIntervals([[maybe_unused]] const char *passName)
142 {
143 #ifdef ENABLE_IR_DUMP
144 if (!GetGraph()->IsAnalysisValid<LivenessAnalyzer>()) {
145 return;
146 }
147 std::ofstream strm(GetFileName(passName, ".li"));
148 if (!strm.is_open()) {
149 std::cerr << errno << " ERROR: " << strerror(errno) << "\n" << GetFileName(passName, ".li") << std::endl;
150 }
151
152 ASSERT(strm.is_open());
153 GetGraph()->GetAnalysis<LivenessAnalyzer>().DumpLifeIntervals(strm);
154 #endif // ENABLE_IR_DUMP
155 }
InitialDumpVisualizerGraph()156 void PassManager::InitialDumpVisualizerGraph()
157 {
158 #ifdef ENABLE_IR_DUMP
159 std::ofstream strm(GetFileName());
160 strm << "begin_compilation\n";
161 strm << " name \"" << GetGraph()->GetRuntime()->GetClassNameFromMethod(GetGraph()->GetMethod()) << "_"
162 << GetGraph()->GetRuntime()->GetMethodName(GetGraph()->GetMethod()) << "\"\n";
163 strm << " method \"" << GetGraph()->GetRuntime()->GetClassNameFromMethod(GetGraph()->GetMethod()) << "_"
164 << GetGraph()->GetRuntime()->GetMethodName(GetGraph()->GetMethod()) << "\"\n";
165 strm << " date " << std::time(nullptr) << "\n";
166 strm << "end_compilation\n";
167 strm.close();
168 #endif // ENABLE_IR_DUMP
169 }
170
DumpVisualizerGraph(const char * passName)171 void PassManager::DumpVisualizerGraph([[maybe_unused]] const char *passName)
172 {
173 #ifdef ENABLE_IR_DUMP
174 std::ofstream strm(GetFileName(), std::ios::app);
175 VisualizerPrinter(GetGraph(), &strm, passName).Print();
176 strm.close();
177 #endif // ENABLE_IR_DUMP
178 }
179
RunPass(Pass * pass,size_t localMemSizeBeforePass)180 bool PassManager::RunPass(Pass *pass, size_t localMemSizeBeforePass)
181 {
182 if (pass->IsAnalysis() && pass->IsValid()) {
183 return true;
184 }
185
186 if (!pass->IsAnalysis() && !static_cast<Optimization *>(pass)->IsEnable()) {
187 return false;
188 }
189
190 if (!IsCheckMode()) {
191 stats_->ProcessBeforeRun(*pass);
192 if (firstExecution_ && GetGraph()->GetParentGraph() == nullptr) {
193 StartExecution();
194 firstExecution_ = false;
195 }
196 }
197
198 #ifndef NDEBUG
199 if (g_options.IsCompilerEnableTracing()) {
200 trace::BeginTracePoint(pass->GetPassName());
201 }
202 #endif // NDEBUG
203
204 bool result = pass->Run();
205
206 #ifndef NDEBUG
207 if (g_options.IsCompilerEnableTracing()) {
208 trace::EndTracePoint();
209 }
210 #endif // NDEBUG
211
212 if (!IsCheckMode()) {
213 ASSERT(graph_->GetLocalAllocator()->GetAllocatedSize() >= localMemSizeBeforePass);
214 stats_->ProcessAfterRun(graph_->GetLocalAllocator()->GetAllocatedSize() - localMemSizeBeforePass);
215 }
216
217 if (pass->IsAnalysis()) {
218 pass->SetValid(result);
219 }
220 bool isCodegen = std::string("Codegen") == pass->GetPassName();
221 if (g_options.IsCompilerDump() && pass->ShouldDump() && !IsCheckMode()) {
222 if (!g_options.IsCompilerDumpFinal() || isCodegen) {
223 DumpGraph(pass->GetPassName());
224 }
225 }
226
227 if (g_options.IsCompilerVisualizerDump() && pass->ShouldDump()) {
228 DumpVisualizerGraph(pass->GetPassName());
229 }
230
231 result &= RunPassChecker(pass, result, isCodegen);
232
233 return result;
234 }
235
RunPassChecker(Pass * pass,bool result,bool isCodegen)236 bool PassManager::RunPassChecker(Pass *pass, bool result, bool isCodegen)
237 {
238 if (graph_->IsAbcKit()) {
239 return RunPassChecker<true>(pass, result, isCodegen);
240 }
241 #ifndef NDEBUG
242 RunPassChecker<false>(pass, result, isCodegen);
243 #endif
244 return true;
245 }
246
247 template <bool FORCE_RUN>
RunPassChecker(Pass * pass,bool result,bool isCodegen)248 bool PassManager::RunPassChecker(Pass *pass, bool result, bool isCodegen)
249 {
250 bool checkerEnabled = g_options.IsCompilerCheckGraph();
251 if (g_options.IsCompilerCheckFinal()) {
252 checkerEnabled = isCodegen;
253 }
254 if constexpr (FORCE_RUN) {
255 checkerEnabled = true;
256 }
257 if (result && !pass->IsAnalysis() && checkerEnabled) {
258 result &= GraphChecker(graph_, pass->GetPassName()).Check();
259 }
260 return result;
261 }
262
GetAllocator()263 ArenaAllocator *PassManager::GetAllocator()
264 {
265 return graph_->GetAllocator();
266 }
267
GetLocalAllocator()268 ArenaAllocator *PassManager::GetLocalAllocator()
269 {
270 return graph_->GetLocalAllocator();
271 }
272
Finalize() const273 void PassManager::Finalize() const
274 {
275 if (g_options.IsCompilerPrintStats()) {
276 stats_->PrintStatistics();
277 }
278 if (g_options.WasSetCompilerDumpStatsCsv()) {
279 stats_->DumpStatisticsCsv();
280 }
281 }
282 } // namespace ark::compiler
283