• 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     }
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