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