1 /*
2 * Copyright (c) 2023-2024 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 "assembler/assembly-emitter.h"
17 #include "canonicalization.h"
18 #include "codegen.h"
19 #include "common.h"
20 #include "compiler/optimizer/optimizations/lowering.h"
21 #include "compiler/optimizer/optimizations/regalloc/reg_alloc_linear_scan.h"
22 #include "reg_encoder.h"
23
24 namespace ark::bytecodeopt::test {
25
26 // NOLINTBEGIN(readability-magic-numbers)
27
TEST_F(CommonTest,RegEncoderF32)28 TEST_F(CommonTest, RegEncoderF32)
29 {
30 auto graph = CreateEmptyGraph();
31 GRAPH(graph)
32 {
33 CONSTANT(0U, 0.0).f32();
34 CONSTANT(1U, 0.0).f32();
35 CONSTANT(2U, 0.0).f32();
36 CONSTANT(3U, 0.0).f32();
37 CONSTANT(4U, 0.0).f32();
38 CONSTANT(5U, 0.0).f32();
39 CONSTANT(6U, 0.0).f32();
40 CONSTANT(7U, 0.0).f32();
41 CONSTANT(8U, 0.0).f32();
42 CONSTANT(9U, 0.0).f32();
43 CONSTANT(10U, 0.0).f32();
44 CONSTANT(11U, 0.0).f32();
45 CONSTANT(12U, 0.0).f32();
46 CONSTANT(13U, 0.0).f32();
47 CONSTANT(14U, 0.0).f32();
48 CONSTANT(15U, 0.0).f32();
49 CONSTANT(16U, 0.0).f32();
50 CONSTANT(17U, 0.0).f32();
51 CONSTANT(18U, 0.0).f32();
52 CONSTANT(19U, 0.0).f32();
53 CONSTANT(20U, 0.0).f32();
54 CONSTANT(21U, 0.0).f32();
55 CONSTANT(22U, 0.0).f32();
56 CONSTANT(23U, 0.0).f32();
57 CONSTANT(24U, 0.0).f32();
58 CONSTANT(25U, 0.0).f32();
59 CONSTANT(26U, 0.0).f32();
60 CONSTANT(27U, 0.0).f32();
61 CONSTANT(28U, 0.0).f32();
62 CONSTANT(29U, 0.0).f32();
63 CONSTANT(30U, 0.0).f32();
64 CONSTANT(31U, 0.0).f32();
65
66 CONSTANT(32U, 1.0).f64();
67 CONSTANT(33U, 2.0_D).f64();
68
69 BASIC_BLOCK(2U, -1L)
70 {
71 // NOLINTNEXTLINE(google-build-using-namespace)
72 using namespace compiler::DataType;
73 INST(40U, Opcode::Sub).f64().Inputs(32U, 33U);
74 INST(41U, Opcode::Return).f64().Inputs(40U);
75 }
76 }
77
78 EXPECT_TRUE(graph->RunPass<compiler::RegAllocLinearScan>(compiler::EmptyRegMask()));
79 EXPECT_TRUE(graph->RunPass<RegEncoder>());
80 EXPECT_TRUE(graph->RunPass<compiler::Cleanup>());
81
82 auto expected = CreateEmptyGraph();
83 GRAPH(expected)
84 {
85 CONSTANT(32U, 1.0).f64();
86 CONSTANT(33U, 2.0_D).f64();
87
88 BASIC_BLOCK(2U, -1L)
89 {
90 // NOLINTNEXTLINE(google-build-using-namespace)
91 using namespace compiler::DataType;
92 INST(40U, Opcode::Sub).f64().Inputs(32U, 33U);
93 INST(41U, Opcode::Return).f64().Inputs(40U);
94 }
95 }
96
97 EXPECT_TRUE(GraphComparator().Compare(graph, expected));
98 }
99
TEST_F(CommonTest,RegEncoderHoldingSpillFillInst)100 TEST_F(CommonTest, RegEncoderHoldingSpillFillInst)
101 {
102 RuntimeInterfaceMock interface(2U);
103 auto graph = CreateEmptyGraph();
104 graph->SetRuntime(&interface);
105 GRAPH(graph)
106 {
107 // NOLINTNEXTLINE(google-build-using-namespace)
108 using namespace compiler::DataType;
109
110 PARAMETER(0U, 0U).ref();
111 PARAMETER(1U, 1U).b();
112 CONSTANT(26U, 0xfffffffffffffffaU).s64();
113 CONSTANT(27U, 0x6U).s64();
114
115 BASIC_BLOCK(2U, 3U, 4U)
116 {
117 INST(4U, Opcode::LoadObject).s64().Inputs(0U);
118 INST(10U, Opcode::LoadObject).s64().Inputs(0U);
119 INST(11U, Opcode::Add).s64().Inputs(10U, 4U);
120 CONSTANT(52U, 0x5265c00U).s64();
121 INST(15U, Opcode::Div).s64().Inputs(11U, 52U);
122 INST(16U, Opcode::Mul).s64().Inputs(15U, 52U);
123 INST(20U, Opcode::Sub).s64().Inputs(16U, 10U);
124 CONSTANT(53U, 0x2932e00U).s64();
125 INST(22U, Opcode::Add).s64().Inputs(20U, 53U);
126 INST(25U, Opcode::IfImm).SrcType(BOOL).CC(compiler::CC_EQ).Imm(0U).Inputs(1U);
127 }
128
129 BASIC_BLOCK(3U, 4U) {}
130
131 BASIC_BLOCK(4U, -1L)
132 {
133 INST(28U, Opcode::Phi).s64().Inputs(26U, 27U);
134 CONSTANT(54U, 0x36ee80U).s64();
135 INST(31U, Opcode::Mul).s64().Inputs(28U, 54U);
136 INST(32U, Opcode::Add).s64().Inputs(31U, 22U);
137 INST(33U, Opcode::SaveState).NoVregs();
138 INST(35U, Opcode::CallVirtual).v0id().Inputs({{REFERENCE, 0U}, {INT64, 32U}, {NO_TYPE, 33U}});
139 INST(36U, Opcode::SaveState).NoVregs();
140 INST(37U, Opcode::LoadAndInitClass).ref().Inputs(36U);
141 INST(39U, Opcode::SaveState).NoVregs();
142 INST(58U, Opcode::InitObject).ref().Inputs({{REFERENCE, 37U}, {REFERENCE, 0U}, {NO_TYPE, 39U}});
143 CONSTANT(55U, 0.0093026_D).f64();
144 CONSTANT(56U, 0.0098902_D).f64();
145 CONSTANT(57U, 0x1388U).s64();
146 INST(45U, Opcode::SaveState).NoVregs();
147 INST(47U, Opcode::CallStatic)
148 .s64()
149 .Inputs({{REFERENCE, 0},
150 {REFERENCE, 58U},
151 {BOOL, 1U},
152 {FLOAT64, 55U},
153 {FLOAT64, 56U},
154 {INT64, 57U},
155 {NO_TYPE, 45U}});
156 INST(48U, Opcode::SaveState).NoVregs();
157 INST(50U, Opcode::CallVirtual).v0id().Inputs({{REFERENCE, 0U}, {INT64, 4U}, {NO_TYPE, 48U}});
158 INST(51U, Opcode::Return).s64().Inputs(47U);
159 }
160 }
161
162 EXPECT_TRUE(graph->RunPass<compiler::RegAllocLinearScan>(compiler::EmptyRegMask()));
163 EXPECT_TRUE(graph->RunPass<RegEncoder>());
164 auto expected = CreateEmptyGraph();
165 GRAPH(expected)
166 {
167 // NOLINTNEXTLINE(google-build-using-namespace)
168 using namespace compiler::DataType;
169
170 PARAMETER(0U, 0U).ref();
171 PARAMETER(1U, 1U).b();
172 CONSTANT(26U, 0xfffffffffffffffaU).s64();
173 CONSTANT(27U, 0x6U).s64();
174
175 BASIC_BLOCK(2U, 3U, 4U)
176 {
177 INST(4U, Opcode::LoadObject).s64().Inputs(0U);
178 INST(10U, Opcode::LoadObject).s64().Inputs(0U);
179 INST(11U, Opcode::Add).s64().Inputs(10U, 4U);
180 CONSTANT(52U, 0x5265c00U).s64();
181 INST(15U, Opcode::Div).s64().Inputs(11U, 52U);
182 INST(16U, Opcode::Mul).s64().Inputs(15U, 52U);
183 INST(20U, Opcode::Sub).s64().Inputs(16U, 10U);
184 CONSTANT(53U, 0x2932e00U).s64();
185 INST(22U, Opcode::Add).s64().Inputs(20U, 53U);
186 INST(25U, Opcode::IfImm).SrcType(BOOL).CC(compiler::CC_EQ).Imm(0U).Inputs(1U);
187 }
188
189 BASIC_BLOCK(3U, 4U)
190 {
191 // SpillFill added
192 INST(60U, Opcode::SpillFill);
193 }
194
195 BASIC_BLOCK(4U, -1L)
196 {
197 INST(28U, Opcode::Phi).s64().Inputs(26U, 27U);
198 CONSTANT(54U, 0x36ee80U).s64();
199 INST(31U, Opcode::Mul).s64().Inputs(28U, 54U);
200 INST(32U, Opcode::Add).s64().Inputs(31U, 22U);
201 INST(33U, Opcode::SaveState).NoVregs();
202 INST(35U, Opcode::CallVirtual).v0id().Inputs({{REFERENCE, 0U}, {INT64, 32U}, {NO_TYPE, 33U}});
203 INST(36U, Opcode::SaveState).NoVregs();
204 INST(37U, Opcode::LoadAndInitClass).ref().Inputs(36U);
205 INST(39U, Opcode::SaveState).NoVregs();
206 INST(58U, Opcode::InitObject).ref().Inputs({{REFERENCE, 37U}, {REFERENCE, 0U}, {NO_TYPE, 39U}});
207 CONSTANT(55U, 0.0093026_D).f64();
208 CONSTANT(56U, 0.0098902_D).f64();
209 CONSTANT(57U, 0x1388U).s64();
210 INST(45U, Opcode::SaveState).NoVregs();
211
212 // SpillFill added
213 INST(70U, Opcode::SpillFill);
214 INST(47U, Opcode::CallStatic)
215 .s64()
216 .Inputs({{REFERENCE, 0},
217 {REFERENCE, 58U},
218 {BOOL, 1U},
219 {FLOAT64, 55U},
220 {FLOAT64, 56U},
221 {INT64, 57U},
222 {NO_TYPE, 45U}});
223 INST(48U, Opcode::SaveState).NoVregs();
224 INST(50U, Opcode::CallVirtual).v0id().Inputs({{REFERENCE, 0U}, {INT64, 4U}, {NO_TYPE, 48U}});
225 INST(51U, Opcode::Return).s64().Inputs(47U);
226 }
227 }
228
229 EXPECT_TRUE(GraphComparator().Compare(graph, expected));
230 }
231
TEST_F(CommonTest,RegEncoderStoreObject)232 TEST_F(CommonTest, RegEncoderStoreObject)
233 {
234 // This test covers function CheckWidthVisitor::VisitStoreObject
235 RuntimeInterfaceMock interface(4U);
236 auto graph = CreateEmptyGraph();
237 graph->SetRuntime(&interface);
238 GRAPH(graph)
239 {
240 PARAMETER(0U, 0U).ref();
241 PARAMETER(1U, 1U).ref();
242 PARAMETER(2U, 2U).ref();
243 PARAMETER(3U, 3U).s32();
244
245 BASIC_BLOCK(2U, -1L)
246 {
247 // NOLINTNEXTLINE(google-build-using-namespace)
248 using namespace compiler::DataType;
249
250 INST(4U, Opcode::SaveState).NoVregs();
251 INST(6U, Opcode::CallStatic).v0id().Inputs({{REFERENCE, 0U}, {NO_TYPE, 4U}});
252 INST(9U, Opcode::StoreObject).ref().Inputs(0U, 2U);
253 INST(12U, Opcode::StoreObject).s32().Inputs(0U, 3U);
254 INST(15U, Opcode::LoadObject).ref().Inputs(1U);
255 INST(16U, Opcode::SaveState).NoVregs();
256 INST(18U, Opcode::CallVirtual).ref().Inputs({{REFERENCE, 15U}, {NO_TYPE, 16U}});
257 INST(19U, Opcode::SaveState).NoVregs();
258 INST(21U, Opcode::LoadClass).ref().Inputs(19U);
259 INST(20U, Opcode::CheckCast).Inputs(18U, 21U, 19U);
260 INST(23U, Opcode::StoreObject).ref().Inputs(0U, 18U);
261 INST(26U, Opcode::LoadObject).ref().Inputs(1U);
262 INST(27U, Opcode::SaveState).NoVregs();
263 INST(29U, Opcode::CallVirtual).ref().Inputs({{REFERENCE, 26U}, {NO_TYPE, 27U}});
264 INST(30U, Opcode::SaveState).NoVregs();
265 INST(32U, Opcode::LoadClass).ref().Inputs(30U);
266 INST(31U, Opcode::CheckCast).Inputs(29U, 32U, 30U);
267 INST(34U, Opcode::StoreObject).ref().Inputs(0U, 29U);
268 INST(37U, Opcode::LoadObject).ref().Inputs(1U);
269 INST(38U, Opcode::SaveState).NoVregs();
270 INST(40U, Opcode::CallVirtual).ref().Inputs({{REFERENCE, 37U}, {NO_TYPE, 38U}});
271 INST(41U, Opcode::SaveState).NoVregs();
272 INST(43U, Opcode::LoadClass).ref().Inputs(41U);
273 INST(42U, Opcode::CheckCast).Inputs(40U, 43U, 41U);
274 INST(45U, Opcode::StoreObject).ref().Inputs(0U, 40U);
275 INST(48U, Opcode::LoadObject).ref().Inputs(1U);
276 INST(49U, Opcode::SaveState).NoVregs();
277 INST(51U, Opcode::CallVirtual).ref().Inputs({{REFERENCE, 48U}, {NO_TYPE, 49U}});
278 INST(52U, Opcode::SaveState).NoVregs();
279 INST(54U, Opcode::LoadClass).ref().Inputs(52U);
280 INST(53U, Opcode::CheckCast).Inputs(51U, 54U, 52U);
281 INST(56U, Opcode::StoreObject).ref().Inputs(0U, 51U);
282 INST(57U, Opcode::ReturnVoid).v0id();
283 }
284 }
285
286 EXPECT_TRUE(graph->RunPass<compiler::RegAllocLinearScan>(compiler::EmptyRegMask()));
287 EXPECT_TRUE(graph->RunPass<RegEncoder>());
288 EXPECT_FALSE(graph->RunPass<compiler::Cleanup>());
289
290 auto expected = CreateEmptyGraph();
291 GRAPH(expected)
292 {
293 PARAMETER(0U, 0U).ref();
294 PARAMETER(1U, 1U).ref();
295 PARAMETER(2U, 2U).ref();
296 PARAMETER(3U, 3U).s32();
297
298 BASIC_BLOCK(2U, -1L)
299 {
300 // NOLINTNEXTLINE(google-build-using-namespace)
301 using namespace compiler::DataType;
302
303 INST(4U, Opcode::SaveState).NoVregs();
304 INST(6U, Opcode::CallStatic).v0id().Inputs({{REFERENCE, 0U}, {NO_TYPE, 4U}});
305 INST(9U, Opcode::StoreObject).ref().Inputs(0U, 2U);
306 INST(12U, Opcode::StoreObject).s32().Inputs(0U, 3U);
307 INST(15U, Opcode::LoadObject).ref().Inputs(1U);
308 INST(16U, Opcode::SaveState).NoVregs();
309 INST(18U, Opcode::CallVirtual).ref().Inputs({{REFERENCE, 15U}, {NO_TYPE, 16U}});
310 INST(60U, Opcode::SaveState).NoVregs();
311 INST(21U, Opcode::LoadClass).ref().Inputs(60U);
312 INST(20U, Opcode::CheckCast).Inputs(18U, 21U, 60U);
313 INST(23U, Opcode::StoreObject).ref().Inputs(0U, 18U);
314 INST(26U, Opcode::LoadObject).ref().Inputs(1U);
315 INST(27U, Opcode::SaveState).NoVregs();
316 INST(29U, Opcode::CallVirtual).ref().Inputs({{REFERENCE, 26U}, {NO_TYPE, 27U}});
317 INST(63U, Opcode::SaveState).NoVregs();
318 INST(32U, Opcode::LoadClass).ref().Inputs(63U);
319 INST(31U, Opcode::CheckCast).Inputs(29U, 32U, 63U);
320 INST(34U, Opcode::StoreObject).ref().Inputs(0U, 29U);
321 INST(37U, Opcode::LoadObject).ref().Inputs(1U);
322 INST(38U, Opcode::SaveState).NoVregs();
323 INST(40U, Opcode::CallVirtual).ref().Inputs({{REFERENCE, 37U}, {NO_TYPE, 38U}});
324 INST(41U, Opcode::SaveState).NoVregs();
325 INST(43U, Opcode::LoadClass).ref().Inputs(41U);
326 INST(42U, Opcode::CheckCast).Inputs(40U, 43U, 41U);
327 INST(45U, Opcode::StoreObject).ref().Inputs(0U, 40U);
328 INST(48U, Opcode::LoadObject).ref().Inputs(1U);
329 INST(49U, Opcode::SaveState).NoVregs();
330 INST(51U, Opcode::CallVirtual).ref().Inputs({{REFERENCE, 48U}, {NO_TYPE, 49U}});
331 INST(52U, Opcode::SaveState).NoVregs();
332 INST(54U, Opcode::LoadClass).ref().Inputs(52U);
333 INST(53U, Opcode::CheckCast).Inputs(51U, 54U, 52U);
334 INST(56U, Opcode::StoreObject).ref().Inputs(0U, 51U);
335 INST(57U, Opcode::ReturnVoid).v0id();
336 }
337 }
338
339 EXPECT_TRUE(GraphComparator().Compare(graph, expected));
340 }
341
342 // Check processing instructions with the same args by RegEncoder.
TEST_F(CommonTest,RegEncoderSameArgsInst)343 TEST_F(CommonTest, RegEncoderSameArgsInst)
344 {
345 auto srcGraph = CreateEmptyGraph();
346 ArenaVector<bool> regMask(254U, false, srcGraph->GetLocalAllocator()->Adapter());
347 srcGraph->InitUsedRegs<compiler::DataType::INT64>(®Mask);
348 GRAPH(srcGraph)
349 {
350 PARAMETER(0U, 0U).ref();
351 PARAMETER(1U, 1U).s32();
352 PARAMETER(2U, 2U).s32();
353 BASIC_BLOCK(2U, -1L)
354 {
355 INST(3U, Opcode::StoreArray).s32().Inputs(0U, 1U, 2U).SrcReg(0U, 17U).SrcReg(1U, 17U).SrcReg(2U, 5U);
356 INST(4U, Opcode::ReturnVoid).v0id();
357 }
358 }
359
360 srcGraph->RunPass<RegEncoder>();
361
362 auto optGraph = CreateEmptyGraph();
363 optGraph->InitUsedRegs<compiler::DataType::INT64>(®Mask);
364 GRAPH(optGraph)
365 {
366 PARAMETER(0U, 0U).ref();
367 PARAMETER(1U, 1U).s32();
368 PARAMETER(2U, 2U).s32();
369 BASIC_BLOCK(2U, -1L)
370 {
371 INST(3U, Opcode::SpillFill);
372 INST(4U, Opcode::StoreArray).s32().Inputs(0U, 1U, 2U);
373 INST(5U, Opcode::ReturnVoid).v0id();
374 }
375 }
376
377 ASSERT_TRUE(GraphComparator().Compare(srcGraph, optGraph));
378
379 for (auto bb : srcGraph->GetBlocksRPO()) {
380 for (auto inst : bb->AllInstsSafe()) {
381 if (inst->GetOpcode() == Opcode::StoreArray) {
382 ASSERT_TRUE(inst->GetSrcReg(0U) == inst->GetSrcReg(1U));
383 }
384 }
385 }
386 }
387
388 // NOLINTEND(readability-magic-numbers)
389
390 } // namespace ark::bytecodeopt::test
391