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