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 */
HWTEST_F(SplitResolverTest, split_resolver_test_001, TestSize.Level1)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;
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 */
HWTEST_F(SplitResolverTest, split_resolver_test_002, TestSize.Level1)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;
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 */
HWTEST_F(SplitResolverTest, split_resolver_test_003, TestSize.Level1)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;
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