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 */ 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); __anona474555b0102(Graph* graph, std::string &method_name) 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 : 12"); 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 */ 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, __anona474555b0202(Graph* graph, std::string &method_name) 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 */ 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, __anona474555b0402(Graph* graph, std::string &method_name) 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 */ 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, __anona474555b0502(Graph* graph, std::string &method_name) 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 */ 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, __anona474555b0602(Graph* graph, std::string &method_name) 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