1 /*
2 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development 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 * Copyright (c) 2024 Huawei Device Co., Ltd.
16 * Licensed under the Apache License, Version 2.0 (the "License");
17 * you may not use this file except in compliance with the License.
18 * You may obtain a copy of the License at
19
20 * http://www.apache.org/licenses/LICENSE-2.0
21 *
22 * Unless required by applicable law or agreed to in writing, software
23 * distributed under the License is distributed on an "AS IS" BASIS,
24 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25 * See the License for the specific language governing permissions and
26 * limitations under the License.
27 */
28
29 #include <cstddef>
30 #include <gtest/gtest.h>
31 #include <filesystem>
32 #include <fstream>
33 #include <iostream>
34 #include <regex>
35 #include <sstream>
36 #include <string>
37 #include <vector>
38
39 #include "compiler_options.h"
40 #include "graph_test.h"
41 #include "optimizer/ir/graph.h"
42 #include "optimizer/optimizations/cleanup.h"
43 #include "optimizer/optimizations/lowering.h"
44 #include "optimizer/optimizations/move_constants.h"
45 #include "optimizer/optimizations/regalloc/reg_alloc.h"
46 #include "optimizer/optimizations/vn.h"
47 #include "optimizer/pass_manager.h"
48 #include "optimizer/pass_manager_statistics.h"
49 #include "reg_acc_alloc.h"
50
51 using namespace testing::ext;
52
53 namespace panda::compiler {
54 class PassManagerTest : public testing::Test {
55 public:
SetUpTestCase(void)56 static void SetUpTestCase(void) {}
TearDownTestCase(void)57 static void TearDownTestCase(void) {}
58
SetUp()59 void SetUp()
60 {
61 option_enable_ir_stats_ = options.IsCompilerEnableIrStats();
62 option_print_stats_ = options.IsCompilerPrintStats();
63 option_reset_local_allocator_ = options.IsCompilerResetLocalAllocator();
64 option_dump_final_ = options.IsCompilerDumpFinal();
65 option_dump_life_intervals_ = options.IsCompilerDumpLifeIntervals();
66 option_dump_stats_csv_ = options.GetCompilerDumpStatsCsv();
67 option_dump_folder_ = options.GetCompilerDumpFolder();
68 }
69
TearDown()70 void TearDown()
71 {
72 CleanDumpFiles();
73 RestoreCompilerOptions();
74 }
75
CleanDumpFiles()76 static void CleanDumpFiles()
77 {
78 std::filesystem::remove(GRAPH_TEST_ABC_DIR "pass_manager_test_002.csv");
79 std::filesystem::remove_all(GRAPH_TEST_ABC_DIR "/pass_manager_test_003");
80 std::filesystem::remove_all(GRAPH_TEST_ABC_DIR "/pass_manager_test_004");
81 std::filesystem::remove_all(GRAPH_TEST_ABC_DIR "/pass_manager_test_005");
82 }
83
RestoreCompilerOptions()84 void RestoreCompilerOptions()
85 {
86 options.SetCompilerEnableIrStats(option_enable_ir_stats_);
87 options.SetCompilerPrintStats(option_print_stats_);
88 options.SetCompilerResetLocalAllocator(option_reset_local_allocator_);
89 options.SetCompilerDumpFinal(option_dump_final_);
90 options.SetCompilerDumpLifeIntervals(option_dump_life_intervals_);
91 options.SetCompilerDumpStatsCsv(option_dump_stats_csv_);
92 options.SetCompilerDumpFolder(option_dump_folder_);
93 }
94
95 // The file name matching rules are as follows
96 // because the file name is generated by the PassManager::GetFileName method
97 // The first is the execution_counter variable, which indicates the number of RunPass executions
98 // When in debug mode, the GraphChecker(graph_).Check() method is executed, which runs RunPass several times
99 // Therefore, when verifying whether the file is generated correctly
100 // only the other parts of the file match except execution_counter
101 // for example : when file:"2_b_c.txt" target_file:"_b_c.txt" return true
FileNameEquals(const std::string &file, const std::string &target_file)102 static bool FileNameEquals(const std::string &file, const std::string &target_file)
103 {
104 size_t length = target_file.length();
105 if (file.length() < length) {
106 return false;
107 }
108 return file.substr(file.length() - length) == target_file;
109 }
110
FileExists(const std::string &path, const std::string &filename)111 static bool FileExists(const std::string &path, const std::string &filename)
112 {
113 for (const auto &entry : std::filesystem::directory_iterator(path)) {
114 std::string file = entry.path().filename().string();
115 if (FileNameEquals(file, filename)) {
116 return true;
117 }
118 }
119 return false;
120 }
121
122 bool option_enable_ir_stats_;
123 bool option_print_stats_;
124 bool option_reset_local_allocator_;
125 bool option_dump_final_;
126 bool option_dump_life_intervals_;
127 std::string option_dump_stats_csv_;
128 std::string option_dump_folder_;
129
130 GraphTest graph_test_;
131 };
132
133 /**
134 * @tc.name: pass_manager_test_001
135 * @tc.desc: Verify the PrintStatistics function.
136 * @tc.type: FUNC
137 * @tc.require:
138 */
HWTEST_F(PassManagerTest, pass_manager_test_001, TestSize.Level1)139 HWTEST_F(PassManagerTest, pass_manager_test_001, TestSize.Level1)
140 {
141 std::string pfile = GRAPH_TEST_ABC_DIR "graphTest.abc";
142 const char *test_method_name = "loop1";
143 bool status = false;
144 options.SetCompilerEnableIrStats(false);
145 options.SetCompilerPrintStats(true);
146 graph_test_.TestBuildGraphFromFile(pfile, [&test_method_name, &status](Graph* graph, std::string &method_name) {
147 if (test_method_name != method_name) {
148 return;
149 }
150
151 graph->RunPass<Cleanup>();
152 graph->RunPass<Lowering>();
153
154 std::stringstream ss;
155 auto old_buf = std::cerr.rdbuf();
156 std::cerr.rdbuf(ss.rdbuf());
157
158 graph->GetPassManager()->Finalize();
159
160 std::cerr.rdbuf(old_buf);
161
162 std::string line;
163 std::vector<std::string> lines;
164 while (std::getline(ss, line)) {
165 lines.emplace_back(line);
166 }
167 constexpr size_t ID_PASSNAME_COLUMN_WIDTH = 40;
168 EXPECT_EQ(lines[0], " ID Pass Name : IR mem Local mem Time,us");
169 EXPECT_EQ(lines[1], "-----------------------------------------------------------------------------");
170 EXPECT_EQ(lines[2].substr(0, ID_PASSNAME_COLUMN_WIDTH), " 0 IrBuilder :");
171 EXPECT_EQ(lines[3].substr(0, ID_PASSNAME_COLUMN_WIDTH), " 1 RPO :");
172 EXPECT_EQ(lines[4].substr(0, ID_PASSNAME_COLUMN_WIDTH), " 2 DominatorTree :");
173 EXPECT_EQ(lines[5].substr(0, ID_PASSNAME_COLUMN_WIDTH), " 3 LoopAnalysis :");
174 EXPECT_EQ(lines[6].substr(0, ID_PASSNAME_COLUMN_WIDTH), " 4 LoopAnalysis :");
175 EXPECT_EQ(lines[7].substr(0, ID_PASSNAME_COLUMN_WIDTH), " 5 Cleanup :");
176 EXPECT_EQ(lines[8].substr(0, ID_PASSNAME_COLUMN_WIDTH), " 6 Lowering :");
177 EXPECT_EQ(lines[9], "-----------------------------------------------------------------------------");
178 EXPECT_EQ(lines[10].substr(0, ID_PASSNAME_COLUMN_WIDTH), " TOTAL:");
179 EXPECT_EQ(lines[11], "PBC instruction number : 13");
180
181 status = true;
182 });
183 EXPECT_TRUE(status);
184 }
185
186 /**
187 * @tc.name: pass_manager_test_002
188 * @tc.desc: Verify the DumpStatisticsCsv function.
189 * @tc.type: FUNC
190 * @tc.require:
191 */
HWTEST_F(PassManagerTest, pass_manager_test_002, TestSize.Level1)192 HWTEST_F(PassManagerTest, pass_manager_test_002, TestSize.Level1)
193 {
194 std::string pfile = GRAPH_TEST_ABC_DIR "graphTest.abc";
195 std::string csv_file = GRAPH_TEST_ABC_DIR "pass_manager_test_002.csv";
196 const char *test_method_name = "loop1";
197 bool status = false;
198 options.SetCompilerPrintStats(false);
199 options.SetCompilerDumpStatsCsv(csv_file);
200 options.SetCompilerEnableIrStats(true);
201 options.SetCompilerResetLocalAllocator(false);
202 graph_test_.TestBuildGraphFromFile(pfile,
203 [&test_method_name, &status, &csv_file](Graph* graph, std::string &method_name) {
204 if (test_method_name != method_name) {
205 return;
206 }
207
208 graph->RunPass<MoveConstants>();
209 graph->RunPass<bytecodeopt::RegAccAlloc>();
210 RegAlloc(graph);
211
212 graph->GetPassManager()->Finalize();
213
214 std::ifstream csv(csv_file);
215 std::stringstream ss;
216 ss << csv.rdbuf();
217
218 std::string line;
219 std::vector<std::string> lines;
220 while (std::getline(ss, line)) {
221 lines.emplace_back(line);
222 }
223
224 constexpr size_t PASSNAME_COLUMN_START = sizeof("\"L_GLOBAL;::#*#loop1\"");
225 const auto get_first_two_columns = [](std::string &line) {
226 return line.substr(0, line.find(',', PASSNAME_COLUMN_START));
227 };
228 EXPECT_EQ(get_first_two_columns(lines[0]), "\"L_GLOBAL;::#*#loop1\",IrBuilder");
229 EXPECT_EQ(get_first_two_columns(lines[1]), "\"L_GLOBAL;::#*#loop1\",RPO");
230 EXPECT_EQ(get_first_two_columns(lines[2]), "\"L_GLOBAL;::#*#loop1\",DominatorTree");
231 EXPECT_EQ(get_first_two_columns(lines[3]), "\"L_GLOBAL;::#*#loop1\",LoopAnalysis");
232 EXPECT_EQ(get_first_two_columns(lines[4]), "\"L_GLOBAL;::#*#loop1\",LoopAnalysis");
233 EXPECT_EQ(get_first_two_columns(lines[5]), "\"L_GLOBAL;::#*#loop1\",MoveConstants");
234 EXPECT_EQ(get_first_two_columns(lines[6]), "\"L_GLOBAL;::#*#loop1\",RegAccAlloc");
235 EXPECT_EQ(get_first_two_columns(lines[7]), "\"L_GLOBAL;::#*#loop1\",Cleanup");
236 EXPECT_EQ(get_first_two_columns(lines[8]), "\"L_GLOBAL;::#*#loop1\",RegAllocGraphColoring");
237 EXPECT_EQ(get_first_two_columns(lines[9]), "\"L_GLOBAL;::#*#loop1\",LivenessAnalysis");
238
239 status = true;
240 });
241 EXPECT_TRUE(status);
242 }
243
244 /**
245 * @tc.name: pass_manager_test_003
246 * @tc.desc: Verify the DumpGraph function.
247 * @tc.type: FUNC
248 * @tc.require:
249 */
HWTEST_F(PassManagerTest, pass_manager_test_003, TestSize.Level1)250 HWTEST_F(PassManagerTest, pass_manager_test_003, TestSize.Level1)
251 {
252 std::string pfile = GRAPH_TEST_ABC_DIR "graphTest.abc";
253 std::string outdir = GRAPH_TEST_ABC_DIR "/pass_manager_test_003";
254 const char *test_method_name = "loop1";
255 bool status = false;
256 options.SetCompilerDumpFinal(false);
257 options.SetCompilerDumpFolder(outdir);
258 graph_test_.TestBuildGraphFromFile(pfile,
259 [&test_method_name, &status, &outdir](Graph* graph, std::string &method_name) {
260 if (test_method_name != method_name) {
261 return;
262 }
263
264 options.SetCompilerDump(true);
265 graph->RunPass<ValNum>();
266 graph->RunPass<Cleanup>();
267 options.SetCompilerDump(false);
268
269 std::string expected_gvn = "_pass_0006_L_GLOBAL_____loop1_GVN.ir";
270 std::string expected_cleanup = "_pass_0007_L_GLOBAL_____loop1_Cleanup.ir";
271
272 EXPECT_TRUE(PassManagerTest::FileExists(outdir, expected_gvn));
273 EXPECT_TRUE(PassManagerTest::FileExists(outdir, expected_cleanup));
274
275 status = true;
276 });
277 EXPECT_TRUE(status);
278 }
279
280 /**
281 * @tc.name: pass_manager_test_004
282 * @tc.desc: Verify the DumpLifeIntervals function.
283 * @tc.type: FUNC
284 * @tc.require:
285 */
HWTEST_F(PassManagerTest, pass_manager_test_004, TestSize.Level1)286 HWTEST_F(PassManagerTest, pass_manager_test_004, TestSize.Level1)
287 {
288 std::string pfile = GRAPH_TEST_ABC_DIR "graphTest.abc";
289 std::string outdir = GRAPH_TEST_ABC_DIR "/pass_manager_test_004";
290 const char *test_method_name = "loop1";
291 bool status = false;
292 options.SetCompilerDumpFolder(outdir);
293 graph_test_.TestBuildGraphFromFile(pfile,
294 [&test_method_name, &status, &outdir](Graph* graph, std::string &method_name) {
295 if (test_method_name != method_name) {
296 return;
297 }
298
299 options.SetCompilerDumpLifeIntervals(true);
300 graph->RunPass<bytecodeopt::RegAccAlloc>();
301 RegAlloc(graph);
302 options.SetCompilerDumpLifeIntervals(false);
303
304 EXPECT_TRUE(PassManagerTest::FileExists(outdir, "_pass_0008_L_GLOBAL_____loop1_RegAllocGraphColoring.li"));
305
306 status = true;
307 });
308 EXPECT_TRUE(status);
309 }
310
311 /**
312 * @tc.name: pass_manager_test_005
313 * @tc.desc: Verify the GetFileName function.
314 * @tc.type: FUNC
315 * @tc.require:
316 */
HWTEST_F(PassManagerTest, pass_manager_test_005, TestSize.Level1)317 HWTEST_F(PassManagerTest, pass_manager_test_005, TestSize.Level1)
318 {
319 std::string pfile = GRAPH_TEST_ABC_DIR "graphTest.abc";
320 std::string outdir = GRAPH_TEST_ABC_DIR "/pass_manager_test_005";
321 const char *test_method_name = "loop1";
322 bool status = false;
323 options.SetCompilerDumpFolder(outdir);
324 graph_test_.TestBuildGraphFromFile(pfile,
325 [&test_method_name, &status](Graph* graph, std::string &method_name) {
326 if (test_method_name != method_name) {
327 return;
328 }
329
330 auto filename1 = graph->GetPassManager()->GetFileName("test", ".ir");
331 std::string filename1_expect = "_pass_0005_L_GLOBAL_____loop1_test.ir";
332 filename1 = filename1.substr(filename1.size() - filename1_expect.size());
333 EXPECT_EQ(filename1, filename1_expect);
334
335 GraphMode mode = graph->GetMode();
336 mode.SetOsr(true);
337 graph->SetMode(mode);
338 auto filename2 = graph->GetPassManager()->GetFileName("test", ".ir");
339 std::string filename2_expect = "_pass_0005_L_GLOBAL_____loop1_OSR_test.ir";
340 filename2 = filename2.substr(filename2.size() - filename2_expect.size());
341 EXPECT_EQ(filename2, filename2_expect);
342 mode.SetOsr(false);
343 graph->SetMode(mode);
344
345 auto child_graph = graph->CreateChildGraph(graph->GetMethod());
346 auto filename3 = child_graph->GetPassManager()->GetFileName("child", ".ir");
347 std::string filename3_expect = "_pass_0005_inlined_L_GLOBAL_____loop1_child.ir";
348 filename3 = filename3.substr(filename3.size() - filename3_expect.size());
349 EXPECT_EQ(filename3, filename3_expect);
350
351 auto filename4 = graph->GetPassManager()->GetFileName(nullptr, ".ir");
352 std::string filename4_expect = "_L_GLOBAL_____loop1.ir";
353 filename4 = filename4.substr(filename4.size() - filename4_expect.size());
354 EXPECT_EQ(filename4, filename4_expect);
355
356 status = true;
357 });
358 EXPECT_TRUE(status);
359 }
360 } // namespace panda::compiler
361