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