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 <algorithm> 30 #include <gtest/gtest.h> 31 #include <utility> 32 33 #include "graph_test.h" 34 #include "optimizer/analysis/liveness_analyzer.h" 35 #include "optimizer/ir/basicblock.h" 36 #include "optimizer/ir/constants.h" 37 #include "optimizer/ir/graph.h" 38 #include "optimizer/ir/inst.h" 39 #include "optimizer/ir/locations.h" 40 #include "optimizer/optimizations/regalloc/split_resolver.h" 41 #include "reg_acc_alloc.h" 42 43 using namespace testing::ext; 44 45 namespace panda::compiler { 46 class SplitResolverTest : public testing::Test { 47 public: SetUpTestCase(void)48 static void SetUpTestCase(void) {}; TearDownTestCase(void)49 static void TearDownTestCase(void) {}; SetUp()50 void SetUp() {}; TearDown()51 void TearDown() {}; 52 53 GraphTest graph_test_; 54 55 template<typename Predicate> FindInstInBlock(BasicBlock * bb,Predicate f)56 static Inst* FindInstInBlock(BasicBlock *bb, Predicate f) 57 { 58 ASSERT(bb != nullptr); 59 auto it = std::find_if(bb->AllInsts().begin(), bb->AllInsts().end(), f); 60 if (it != bb->AllInsts().end()) { 61 return *it; 62 } 63 return nullptr; 64 } 65 66 template<typename Predicate> FindInstInGraph(Graph * graph,Predicate f)67 static Inst* FindInstInGraph(Graph *graph, Predicate f) 68 { 69 ASSERT(graph != nullptr); 70 for (auto bb : graph->GetBlocksRPO()) { 71 auto it = std::find_if(bb->AllInsts().begin(), bb->AllInsts().end(), f); 72 if (it != bb->AllInsts().end()) { 73 return *it; 74 } 75 } 76 return nullptr; 77 } 78 79 template <typename Predicate> FindInstWithInterval(const LivenessAnalyzer & la,BasicBlock * bb,Predicate f)80 static std::pair<Inst *, LifeIntervals *> FindInstWithInterval(const LivenessAnalyzer &la, BasicBlock *bb, 81 Predicate f) 82 { 83 auto inst = FindInstInBlock(bb, f); 84 ASSERT(inst != nullptr); 85 auto interval = la.GetInstLifeIntervals(inst); 86 ASSERT(interval != nullptr); 87 return {inst, interval}; 88 } 89 90 template <typename Predicate> FindInstWithInterval(const LivenessAnalyzer & la,Graph * graph,Predicate f)91 static std::pair<Inst *, LifeIntervals *> FindInstWithInterval(const LivenessAnalyzer &la, Graph *graph, 92 Predicate f) 93 { 94 auto inst = FindInstInGraph(graph, f); 95 ASSERT(inst != nullptr); 96 auto interval = la.GetInstLifeIntervals(inst); 97 ASSERT(interval != nullptr); 98 return {inst, interval}; 99 } 100 IsIntrinsic(Inst * inst,IntrinsicInst::IntrinsicId id)101 static bool IsIntrinsic(Inst *inst, IntrinsicInst::IntrinsicId id) 102 { 103 return inst->IsIntrinsic() && inst->CastToIntrinsic()->GetIntrinsicId() == id; 104 } 105 InitUsedRegs(Graph * graph,size_t count)106 static void InitUsedRegs(Graph *graph, size_t count) 107 { 108 ASSERT(graph != nullptr); 109 ArenaVector<bool> used_regs(count, false, graph->GetAllocator()->Adapter()); 110 graph->InitUsedRegs<DataType::INT64>(&used_regs); 111 } 112 }; 113 114 /** 115 * @tc.name: split_resolver_test_001 116 * @tc.desc: Verify the ConnectSiblings function. 117 * @tc.type: FUNC 118 * @tc.require: 119 */ 120 HWTEST_F(SplitResolverTest, split_resolver_test_001, TestSize.Level1) 121 { 122 const Register REG_PARAM_INIT = 0; 123 const StackSlot SLOT_AT_ADD = 0; 124 const Register REG_AT_MUL = 1; 125 126 std::string pfile = GRAPH_TEST_ABC_DIR "regallocTest.abc"; 127 const char *test_method_name = "split1"; 128 bool status = false; __anonca3494300102(Graph* graph, std::string &method_name) 129 graph_test_.TestBuildGraphFromFile(pfile, [&test_method_name, &status](Graph* graph, std::string &method_name) { 130 if (test_method_name != method_name) { 131 return; 132 } 133 134 graph->RunPass<LivenessAnalyzer>(); 135 auto &la = graph->GetAnalysis<LivenessAnalyzer>(); 136 137 auto start_bb = graph->GetStartBlock(); 138 EXPECT_TRUE(start_bb != nullptr); 139 auto bb = start_bb->GetSuccessor(0); 140 EXPECT_TRUE(bb != nullptr); 141 142 auto [param_inst, param_interval] = 143 FindInstWithInterval(la, start_bb, [](Inst *inst) { return inst->IsParameter() && inst->HasUsers(); }); 144 auto [add_inst, add_interval] = FindInstWithInterval(la, bb, [](Inst *inst) { 145 return IsIntrinsic(inst, IntrinsicInst::IntrinsicId::ADD2_IMM8_V8); 146 }); 147 auto [mul_inst, mul_interval] = FindInstWithInterval(la, bb, [](Inst *inst) { 148 return IsIntrinsic(inst, IntrinsicInst::IntrinsicId::MUL2_IMM8_V8); 149 }); 150 param_interval->SetReg(REG_PARAM_INIT); 151 152 // Split param_interval at add inst and mul inst 153 auto split = param_interval->SplitAt(add_interval->GetBegin() - 1, graph->GetAllocator()); 154 split->SetLocation(Location::MakeStackSlot(SLOT_AT_ADD)); 155 split = split->SplitAt(mul_interval->GetBegin() - 1, graph->GetAllocator()); 156 split->SetReg(REG_AT_MUL); 157 158 InitUsedRegs(graph, 256); 159 SplitResolver(graph, &la).Run(); 160 161 auto sf_data1 = add_inst->GetPrev()->CastToSpillFill()->GetSpillFill(0); 162 auto sf_data2 = mul_inst->GetPrev()->CastToSpillFill()->GetSpillFill(0); 163 EXPECT_EQ(sf_data1.GetSrc(), Location::MakeRegister(REG_PARAM_INIT)); 164 EXPECT_EQ(sf_data1.GetDst(), Location::MakeStackSlot(SLOT_AT_ADD)); 165 EXPECT_EQ(sf_data2.GetSrc(), Location::MakeStackSlot(SLOT_AT_ADD)); 166 EXPECT_EQ(sf_data2.GetDst(), Location::MakeRegister(REG_AT_MUL)); 167 168 status = true; 169 }); 170 EXPECT_TRUE(status); 171 } 172 173 /** 174 * @tc.name: split_resolver_test_002 175 * @tc.desc: Verify the ConnectSiblings function with existing spillfill insts. 176 * @tc.type: FUNC 177 * @tc.require: 178 */ 179 HWTEST_F(SplitResolverTest, split_resolver_test_002, TestSize.Level1) 180 { 181 const Register REG_PARAM1_INIT = 0; 182 const Register REG_PARAM2_INIT = 0; 183 const StackSlot SLOT_PARAM1_AT_CALL = 0; 184 const Register REG_PARAM2_AT_CALL = 2; 185 186 std::string pfile = GRAPH_TEST_ABC_DIR "regallocTest.abc"; 187 const char *test_method_name = "split2"; 188 bool status = false; __anonca3494300502(Graph* graph, std::string &method_name) 189 graph_test_.TestBuildGraphFromFile(pfile, [&test_method_name, &status](Graph* graph, std::string &method_name) { 190 if (test_method_name != method_name) { 191 return; 192 } 193 194 // Insert a spillfill inst before add inst to test that 195 // the ConnectSiblings function can skip this spillfill inst 196 auto bb = graph->GetStartBlock()->GetSuccessor(0); 197 auto call_inst = FindInstInBlock( 198 bb, [](Inst *inst) { return IsIntrinsic(inst, IntrinsicInst::IntrinsicId::CALLARGS2_IMM8_V8_V8); }); 199 auto input_fill_inst = graph->CreateInstSpillFill(); 200 input_fill_inst->SetSpillFillType(SpillFillType::INPUT_FILL); 201 call_inst->InsertBefore(input_fill_inst); 202 203 graph->RunPass<LivenessAnalyzer>(); 204 auto &la = graph->GetAnalysis<LivenessAnalyzer>(); 205 206 auto [param1_inst, param1_interval] = FindInstWithInterval( 207 la, graph->GetStartBlock(), [](Inst *inst) { return inst->IsParameter() && inst->HasUsers(); }); 208 auto param2_inst = param1_inst->GetNext(); 209 auto param2_interval = la.GetInstLifeIntervals(param2_inst); 210 auto call_interval = la.GetInstLifeIntervals(call_inst); 211 param1_interval->SetReg(REG_PARAM1_INIT); 212 param2_interval->SetReg(REG_PARAM2_INIT); 213 214 // Split at call inst 215 auto split1 = param1_interval->SplitAt(call_interval->GetBegin() - 1, graph->GetAllocator()); 216 split1->SetLocation(Location::MakeStackSlot(SLOT_PARAM1_AT_CALL)); 217 auto split2 = param2_interval->SplitAt(call_interval->GetBegin() - 1, graph->GetAllocator()); 218 split2->SetReg(REG_PARAM2_AT_CALL); 219 220 InitUsedRegs(graph, 256); 221 SplitResolver(graph, &la).Run(); 222 223 EXPECT_EQ(call_inst->GetPrev(), input_fill_inst); 224 auto sf_data = input_fill_inst->GetPrev()->CastToSpillFill()->GetSpillFills(); 225 EXPECT_EQ(sf_data[0].GetSrc(), Location::MakeRegister(REG_PARAM1_INIT)); 226 EXPECT_EQ(sf_data[0].GetDst(), Location::MakeStackSlot(SLOT_PARAM1_AT_CALL)); 227 EXPECT_EQ(sf_data[1].GetSrc(), Location::MakeRegister(REG_PARAM2_INIT)); 228 EXPECT_EQ(sf_data[1].GetDst(), Location::MakeRegister(REG_PARAM2_AT_CALL)); 229 230 status = true; 231 }); 232 EXPECT_TRUE(status); 233 } 234 235 /** 236 * @tc.name: split_resolver_test_003 237 * @tc.desc: Verify the ConnectSpiltFromPredBlock function. 238 * @tc.type: FUNC 239 * @tc.require: 240 */ 241 HWTEST_F(SplitResolverTest, split_resolver_test_003, TestSize.Level1) 242 { 243 const Register REG_PARAM_INIT = 0; 244 const StackSlot SLOT_AT_MUL = 1; 245 246 std::string pfile = GRAPH_TEST_ABC_DIR "regallocTest.abc"; 247 const char *test_method_name = "split3"; 248 bool status = false; __anonca3494300802(Graph* graph, std::string &method_name) 249 graph_test_.TestBuildGraphFromFile(pfile, [&test_method_name, &status](Graph* graph, std::string &method_name) { 250 if (test_method_name != method_name) { 251 return; 252 } 253 254 graph->RunPass<LivenessAnalyzer>(); 255 auto &la = graph->GetAnalysis<LivenessAnalyzer>(); 256 257 auto [param_inst, param_interval] = FindInstWithInterval( 258 la, graph->GetStartBlock(), [](Inst *inst) { return inst->IsParameter() && inst->HasUsers(); }); 259 auto [mul_inst, mul_interval] = FindInstWithInterval(la, graph, [](Inst *inst) { 260 return IsIntrinsic(inst, IntrinsicInst::IntrinsicId::MUL2_IMM8_V8); 261 }); 262 param_interval->SetReg(REG_PARAM_INIT); 263 264 // Split in `if` branch 265 auto split = param_interval->SplitAt(mul_interval->GetBegin() - 1, graph->GetAllocator()); 266 split->SetLocation(Location::MakeStackSlot(SLOT_AT_MUL)); 267 268 InitUsedRegs(graph, 256); 269 SplitResolver(graph, &la).Run(); 270 271 auto sf_inst1 = mul_inst->GetPrev()->CastToSpillFill(); 272 EXPECT_EQ(sf_inst1->GetSpillFillType(), SpillFillType::CONNECT_SPLIT_SIBLINGS); 273 auto sf_data1 = sf_inst1->CastToSpillFill()->GetSpillFill(0); 274 EXPECT_EQ(sf_data1.GetSrc(), Location::MakeRegister(REG_PARAM_INIT)); 275 EXPECT_EQ(sf_data1.GetDst(), Location::MakeStackSlot(SLOT_AT_MUL)); 276 277 // This spillfill inst should be created at the end of `else` branch 278 auto sf_inst2 = FindInstInGraph(graph, [](Inst *inst) { 279 return inst->IsSpillFill() && inst->CastToSpillFill()->GetSpillFillType() == SpillFillType::SPLIT_MOVE; 280 }); 281 EXPECT_TRUE(sf_inst2 != nullptr); 282 auto sf_data2 = sf_inst2->CastToSpillFill()->GetSpillFill(0); 283 EXPECT_EQ(sf_data2.GetSrc(), Location::MakeRegister(REG_PARAM_INIT)); 284 EXPECT_EQ(sf_data2.GetDst(), Location::MakeStackSlot(SLOT_AT_MUL)); 285 286 status = true; 287 }); 288 EXPECT_TRUE(status); 289 } 290 } // namespace panda::compiler 291