• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device 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 
16 #include "unit_test.h"
17 #include "optimizer/ir/graph_cloner.h"
18 #include "optimizer/optimizations/regalloc/reg_alloc_linear_scan.h"
19 #include "optimizer/optimizations/regalloc/reg_alloc_graph_coloring.h"
20 
21 namespace panda::compiler {
22 class RegAllocCommonTest : public GraphTest {
23 public:
24     template <typename Checker>
RunRegAllocatorsAndCheck(Graph * graph,Checker checker) const25     void RunRegAllocatorsAndCheck(Graph *graph, Checker checker) const
26     {
27         if (graph->GetCallingConvention() == nullptr) {
28             return;
29         }
30         auto graph_ls = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph();
31         ASSERT_TRUE(graph_ls->RunPass<RegAllocLinearScan>());
32         checker(graph_ls);
33 
34         // RegAllocGraphColoring is not supported for AARCH32
35         if (GetGraph()->GetArch() == Arch::AARCH32) {
36             return;
37         }
38         auto graph_gc = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph();
39         ASSERT_TRUE(graph_gc->RunPass<RegAllocGraphColoring>());
40         checker(graph_gc);
41     }
42 
43 protected:
44     template <DataType::Type reg_type>
45     void TestParametersLocations() const;
46 };
47 
48 template <DataType::Type reg_type>
TestParametersLocations() const49 void RegAllocCommonTest::TestParametersLocations() const
50 {
51     auto graph = CreateEmptyGraph();
52     if constexpr (DataType::UINT64 == reg_type) {
53         GRAPH(graph)
54         {
55             PARAMETER(0, 0).ref();
56             PARAMETER(1, 1).u64();
57             PARAMETER(2, 2).u64();
58             PARAMETER(3, 3).u64();
59             PARAMETER(4, 4).u64();
60             PARAMETER(5, 5).u64();
61             PARAMETER(6, 6).u64();
62             PARAMETER(7, 7).u64();
63             PARAMETER(8, 8).u64();
64             PARAMETER(9, 9).u64();
65 
66             BASIC_BLOCK(2, -1)
67             {
68                 INST(30, Opcode::SaveState).Inputs(0).SrcVregs({0});
69                 INST(11, Opcode::NullCheck).ref().Inputs(0, 30);
70                 INST(12, Opcode::StoreObject).u64().Inputs(11, 1);
71                 INST(13, Opcode::StoreObject).u64().Inputs(11, 2);
72                 INST(14, Opcode::StoreObject).u64().Inputs(11, 3);
73                 INST(15, Opcode::StoreObject).u64().Inputs(11, 4);
74                 INST(16, Opcode::StoreObject).u64().Inputs(11, 5);
75                 INST(17, Opcode::StoreObject).u64().Inputs(11, 6);
76                 INST(18, Opcode::StoreObject).u64().Inputs(11, 7);
77                 INST(19, Opcode::StoreObject).u64().Inputs(11, 8);
78                 INST(20, Opcode::StoreObject).u64().Inputs(11, 9);
79                 INST(31, Opcode::ReturnVoid).v0id();
80             }
81         }
82     } else {
83         GRAPH(graph)
84         {
85             PARAMETER(0, 0).ref();
86             PARAMETER(1, 1).u32();
87             PARAMETER(2, 2).u32();
88             PARAMETER(3, 3).u32();
89             PARAMETER(4, 4).u32();
90             PARAMETER(5, 5).u32();
91             PARAMETER(6, 6).u32();
92             PARAMETER(7, 7).u32();
93             PARAMETER(8, 8).u32();
94             PARAMETER(9, 9).u32();
95 
96             BASIC_BLOCK(2, -1)
97             {
98                 INST(30, Opcode::SaveState).Inputs(0).SrcVregs({0});
99                 INST(11, Opcode::NullCheck).ref().Inputs(0, 30);
100                 INST(12, Opcode::StoreObject).u32().Inputs(11, 1);
101                 INST(13, Opcode::StoreObject).u32().Inputs(11, 2);
102                 INST(14, Opcode::StoreObject).u32().Inputs(11, 3);
103                 INST(15, Opcode::StoreObject).u32().Inputs(11, 4);
104                 INST(16, Opcode::StoreObject).u32().Inputs(11, 5);
105                 INST(17, Opcode::StoreObject).u32().Inputs(11, 6);
106                 INST(18, Opcode::StoreObject).u32().Inputs(11, 7);
107                 INST(19, Opcode::StoreObject).u32().Inputs(11, 8);
108                 INST(20, Opcode::StoreObject).u32().Inputs(11, 9);
109                 INST(31, Opcode::ReturnVoid).v0id();
110             }
111         }
112     }
113 
114     RunRegAllocatorsAndCheck(graph, [type = reg_type](Graph *check_graph) {
115         auto arch = check_graph->GetArch();
116         unsigned slot_inc = Is64Bits(type, arch) && !Is64BitsArch(arch) ? 2U : 1U;
117 
118         unsigned params_on_registers = 0;
119         if (Arch::AARCH64 == check_graph->GetArch()) {
120             /**
121              * Test case for Arch::AARCH64:
122              *
123              * - Parameters [arg0 - arg6] are placed in the registers [r1-r7]
124              * - All other Parameters are placed in stack slots [slot0 - ...]
125              */
126             params_on_registers = 7;
127         } else if (Arch::AARCH32 == check_graph->GetArch()) {
128             /**
129              * Test case for Arch::AARCH32:
130              * - ref-Parameter (arg0) is placed in the r1 register
131              * - If arg1 is 64-bit Parameter, it is placed in the [r2-r3] registers
132              * - If arg1, arg2 are 32-bit Parameters, they are placed in the [r2-r3] registers
133              * - All other Parameters are placed in stack slots [slot0 - ...]
134              */
135             params_on_registers = (type == DataType::UINT64) ? 2U : 3U;
136         }
137 
138         std::map<Location, Inst *> assigned_locations;
139         unsigned index = 0;
140         unsigned arg_slot = 0;
141         for (auto param_inst : check_graph->GetParameters()) {
142             // Check intial locations
143             auto src_location = param_inst->CastToParameter()->GetLocationData().GetSrc();
144             if (index < params_on_registers) {
145                 EXPECT_EQ(src_location.GetKind(), LocationType::REGISTER);
146                 EXPECT_EQ(src_location.GetValue(), index + 1U);
147             } else {
148                 EXPECT_EQ(src_location.GetKind(), LocationType::STACK_PARAMETER);
149                 EXPECT_EQ(src_location.GetValue(), arg_slot);
150                 arg_slot += slot_inc;
151             }
152 
153             // Check that assigned locations do not overlap
154             auto dst_location = param_inst->CastToParameter()->GetLocationData().GetDst();
155             EXPECT_EQ(assigned_locations.count(dst_location), 0U);
156             assigned_locations.insert({dst_location, param_inst});
157 
158             index++;
159         }
160     });
161 }
162 
TEST_F(RegAllocCommonTest,ParametersLocation)163 TEST_F(RegAllocCommonTest, ParametersLocation)
164 {
165     TestParametersLocations<DataType::UINT64>();
166     TestParametersLocations<DataType::UINT32>();
167 }
168 
TEST_F(RegAllocCommonTest,LocationsNoSplits)169 TEST_F(RegAllocCommonTest, LocationsNoSplits)
170 {
171     auto graph = CreateEmptyGraph();
172     GRAPH(graph)
173     {
174         CONSTANT(0, 1).s32();
175         CONSTANT(1, 2).s32();
176         CONSTANT(2, 3).s32();
177 
178         BASIC_BLOCK(2, -1)
179         {
180             INST(3, Opcode::Add).u32().Inputs(0, 1);
181             INST(4, Opcode::SaveState).Inputs().SrcVregs({});
182             INST(5, Opcode::CallStatic).InputsAutoType(3, 2, 0, 1, 4).u32();
183             INST(6, Opcode::Return).u32().Inputs(5);
184         }
185     }
186 
187     if (graph->GetArch() == Arch::AARCH32) {
188         // Enable after full registers mask support the ARM32
189         return;
190     }
191 
192     // Check that there are no spill-fills in the graph
193     RunRegAllocatorsAndCheck(graph, [](Graph *check_graph) {
194         for (auto bb : check_graph->GetBlocksRPO()) {
195             for (auto inst : bb->AllInsts()) {
196                 EXPECT_FALSE(inst->IsSpillFill());
197                 if (inst->NoDest()) {
198                     return;
199                 }
200                 EXPECT_NE(inst->GetDstReg(), INVALID_REG);
201             }
202         }
203     });
204 }
205 
TEST_F(RegAllocCommonTest,ImplicitNullCheckStackMap)206 TEST_F(RegAllocCommonTest, ImplicitNullCheckStackMap)
207 {
208     auto graph = CreateEmptyGraph();
209     GRAPH(graph)
210     {
211         PARAMETER(0, 0).ref();
212         PARAMETER(1, 1).ref();
213 
214         BASIC_BLOCK(2, -1)
215         {
216             INST(3, Opcode::SaveState).NoVregs();
217             INST(4, Opcode::LoadAndInitClass).ref().Inputs(3);
218             INST(5, Opcode::NewObject).ref().Inputs(4, 3);
219             INST(6, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1});
220             INST(7, Opcode::NullCheck).ref().Inputs(5, 6);
221             INST(8, Opcode::LoadObject).s32().Inputs(7);
222             INST(9, Opcode::Return).s32().Inputs(8);
223         }
224     }
225     INS(7).CastToNullCheck()->SetImplicit(true);
226 
227     RunRegAllocatorsAndCheck(graph, [](Graph *check_graph) {
228         auto bb = check_graph->GetStartBlock()->GetSuccessor(0);
229         // Find null_check
230         Inst *null_check = nullptr;
231         for (auto inst : bb->AllInsts()) {
232             if (inst->IsNullCheck()) {
233                 null_check = inst;
234                 break;
235             }
236         }
237         ASSERT(null_check != nullptr);
238         auto save_state = null_check->GetSaveState();
239         // Check that save_state's inputs are added to the roots
240         auto roots = save_state->GetRootsRegsMask();
241         for (auto input : save_state->GetInputs()) {
242             auto reg = input.GetInst()->GetDstReg();
243             EXPECT_NE(reg, INVALID_REG);
244             EXPECT_TRUE(roots.test(reg));
245         }
246     });
247 }
248 
TEST_F(RegAllocCommonTest,DynMethodNargsParamReserve)249 TEST_F(RegAllocCommonTest, DynMethodNargsParamReserve)
250 {
251     auto graph = GetGraph();
252     graph->SetDynamicMethod();
253 
254     GRAPH(graph)
255     {
256         PARAMETER(0, 0).any();
257         PARAMETER(1, 1).any();
258 
259         BASIC_BLOCK(2, -1)
260         {
261             INST(2, Opcode::SaveState).NoVregs();
262             INST(3, Opcode::Intrinsic).any().Inputs({{DataType::ANY, 1}, {DataType::ANY, 0}, {DataType::NO_TYPE, 2}});
263             INST(4, Opcode::Return).any().Inputs(3);
264         }
265     }
266 
267     RunRegAllocatorsAndCheck(graph, [](Graph *check_graph) {
268         check_graph->Dump(&std::cout);
269         auto reg = Target(check_graph->GetArch()).GetParamRegId(1);
270 
271         for (auto inst : check_graph->GetStartBlock()->Insts()) {
272             if (inst->IsSpillFill()) {
273                 auto sfs = inst->CastToSpillFill()->GetSpillFills();
274                 auto it = std::find_if(sfs.cbegin(), sfs.cend(), [reg](auto sf) {
275                     return sf.DstValue() == reg && sf.DstType() == LocationType::REGISTER;
276                 });
277                 ASSERT_EQ(it, sfs.cend());
278             }
279         }
280     });
281 }
282 }  // namespace panda::compiler
283