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