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 "libpandabase/utils/utils.h"
17 #include "common.h"
18 #include "check_resolver.h"
19 #include "compiler/optimizer/optimizations/cleanup.h"
20 #include "compiler/optimizer/optimizations/lowering.h"
21
22 namespace ark::bytecodeopt::test {
23
24 class LoweringTest : public CommonTest {};
25
26 // NOLINTBEGIN(readability-magic-numbers)
27
28 // Checks the results of lowering pass if negative operands are used
TEST_F(IrBuilderTest,Lowering)29 TEST_F(IrBuilderTest, Lowering)
30 {
31 // ISA opcodes with expected lowering IR opcodes
32 std::map<std::string, compiler::Opcode> opcodes = {
33 {"add", compiler::Opcode::SubI}, {"sub", compiler::Opcode::AddI}, {"mul", compiler::Opcode::MulI},
34 {"and", compiler::Opcode::AndI}, {"xor", compiler::Opcode::XorI}, {"or", compiler::Opcode::OrI},
35 {"div", compiler::Opcode::DivI}, {"mod", compiler::Opcode::ModI},
36 };
37
38 const std::string templateSource = R"(
39 .function i32 main() {
40 movi v0, 0x3
41 movi v1, 0xffffffffffffffe2
42 OPCODE v0, v1
43 return
44 }
45 )";
46
47 for (auto const &opcode : opcodes) {
48 // Specialize template source to the current opcode
49 std::string source(templateSource);
50 size_t startPos = source.find("OPCODE");
51 source.replace(startPos, 6U, opcode.first);
52
53 ASSERT_TRUE(ParseToGraph(source, "main"));
54 #ifndef NDEBUG
55 GetGraph()->SetLowLevelInstructionsEnabled();
56 #endif
57 GetGraph()->RunPass<CheckResolver>();
58 GetGraph()->RunPass<compiler::Lowering>();
59 GetGraph()->RunPass<compiler::Cleanup>();
60
61 int32_t imm = -30_I;
62 // Note: `AddI -30` is handled as `SubI 30`. `SubI -30` is handled as `AddI 30`.
63 if (opcode.second == compiler::Opcode::AddI || opcode.second == compiler::Opcode::SubI) {
64 imm = 30_I;
65 }
66
67 auto expected = CreateEmptyGraph();
68 GRAPH(expected)
69 {
70 CONSTANT(1U, 3U).s32();
71
72 BASIC_BLOCK(2U, -1L)
73 {
74 INST(2U, opcode.second).s32().Inputs(1U).Imm(imm);
75 INST(3U, Opcode::Return).s32().Inputs(2U);
76 }
77 }
78
79 EXPECT_TRUE(GraphComparator().Compare(GetGraph(), expected));
80 }
81 }
82
83 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST_F(LoweringTest,AddSub)84 TEST_F(LoweringTest, AddSub)
85 {
86 auto init = CreateEmptyGraph();
87 GRAPH(init)
88 {
89 PARAMETER(0U, 0U).u32();
90 PARAMETER(1U, 1U).u64();
91 PARAMETER(2U, 2U).f32();
92 CONSTANT(3U, 12U).s32();
93 CONSTANT(4U, 150U).s32();
94 CONSTANT(5U, 0U).s64();
95 CONSTANT(6U, 1.2F).f32();
96 CONSTANT(7U, -1L).s32();
97
98 BASIC_BLOCK(2U, -1L)
99 {
100 INST(8U, Opcode::Add).u32().Inputs(0U, 3U);
101 INST(9U, Opcode::Sub).u32().Inputs(0U, 3U);
102 INST(10U, Opcode::Add).u32().Inputs(0U, 4U);
103 INST(11U, Opcode::Sub).u32().Inputs(0U, 4U);
104 INST(12U, Opcode::Add).u64().Inputs(1U, 5U);
105 INST(13U, Opcode::Sub).f32().Inputs(2U, 6U);
106 INST(14U, Opcode::Sub).u32().Inputs(0U, 7U);
107 INST(15U, Opcode::SaveState).NoVregs();
108 INST(20U, Opcode::CallStatic).b().InputsAutoType(8U, 9U, 10U, 11U, 12U, 13U, 14U, 15U);
109 INST(21U, Opcode::Return).b().Inputs(20U);
110 }
111 }
112 #ifndef NDEBUG
113 init->SetLowLevelInstructionsEnabled();
114 #endif
115 init->RunPass<compiler::Lowering>();
116 init->RunPass<compiler::Cleanup>();
117
118 auto expected = CreateEmptyGraph();
119 GRAPH(expected)
120 {
121 PARAMETER(0U, 0U).u32();
122 PARAMETER(1U, 1U).u64();
123 PARAMETER(2U, 2U).f32();
124 CONSTANT(4U, 150U).s32();
125 CONSTANT(5U, 0U).s64();
126 CONSTANT(6U, 1.2F).f32();
127
128 BASIC_BLOCK(2U, -1L)
129 {
130 INST(22U, Opcode::AddI).u32().Inputs(0U).Imm(0xcU);
131 INST(23U, Opcode::SubI).u32().Inputs(0U).Imm(0xcU);
132 INST(10U, Opcode::Add).u32().Inputs(0U, 4U);
133 INST(11U, Opcode::Sub).u32().Inputs(0U, 4U);
134 INST(12U, Opcode::Add).u64().Inputs(1U, 5U);
135 INST(13U, Opcode::Sub).f32().Inputs(2U, 6U);
136 INST(24U, Opcode::AddI).u32().Inputs(0U).Imm(1U);
137 INST(19U, Opcode::SaveState).NoVregs();
138 INST(20U, Opcode::CallStatic).b().InputsAutoType(22U, 23U, 10U, 11U, 12U, 13U, 24U, 19U);
139 INST(21U, Opcode::Return).b().Inputs(20U);
140 }
141 }
142 EXPECT_TRUE(GraphComparator().Compare(init, expected));
143 }
144
145 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST_F(LoweringTest,MulDivMod)146 TEST_F(LoweringTest, MulDivMod)
147 {
148 auto init = CreateEmptyGraph();
149 GRAPH(init)
150 {
151 PARAMETER(0U, 0U).u32();
152 PARAMETER(1U, 1U).u64();
153 PARAMETER(2U, 2U).f32();
154 CONSTANT(3U, 12U).s32();
155 CONSTANT(4U, 150U).s32();
156 CONSTANT(5U, 0U).s64();
157 CONSTANT(6U, 1.2F).f32();
158 CONSTANT(7U, -1L).s32();
159
160 BASIC_BLOCK(2U, -1L)
161 {
162 INST(8U, Opcode::Div).s32().Inputs(0U, 3U);
163 INST(9U, Opcode::Div).u32().Inputs(0U, 4U);
164 INST(10U, Opcode::Div).u64().Inputs(1U, 5U);
165 INST(11U, Opcode::Div).f32().Inputs(2U, 6U);
166 INST(12U, Opcode::Div).s32().Inputs(0U, 7U);
167
168 INST(13U, Opcode::Mod).s32().Inputs(0U, 3U);
169 INST(14U, Opcode::Mod).u32().Inputs(0U, 4U);
170 INST(15U, Opcode::Mod).u64().Inputs(1U, 5U);
171 INST(16U, Opcode::Mod).f32().Inputs(2U, 6U);
172 INST(17U, Opcode::Mod).s32().Inputs(0U, 7U);
173
174 INST(18U, Opcode::Mul).s32().Inputs(0U, 3U);
175 INST(19U, Opcode::Mul).u32().Inputs(0U, 4U);
176 INST(20U, Opcode::Mul).u64().Inputs(1U, 5U);
177 INST(21U, Opcode::Mul).f32().Inputs(2U, 6U);
178 INST(22U, Opcode::Mul).s32().Inputs(0U, 7U);
179 INST(31U, Opcode::SaveState).NoVregs();
180 INST(23U, Opcode::CallStatic)
181 .b()
182 .InputsAutoType(8U, 9U, 10U, 11U, 12U, 13U, 14U, 15U, 16U, 17U, 18U, 19U, 20U, 21U, 22U, 31U);
183 INST(24U, Opcode::Return).b().Inputs(23U);
184 }
185 }
186
187 #ifndef NDEBUG
188 init->SetLowLevelInstructionsEnabled();
189 #endif
190 init->RunPass<compiler::Lowering>();
191 init->RunPass<compiler::Cleanup>();
192
193 auto expected = CreateEmptyGraph();
194 GRAPH(expected)
195 {
196 PARAMETER(0U, 0U).u32();
197 PARAMETER(1U, 1U).u64();
198 PARAMETER(2U, 2U).f32();
199 CONSTANT(4U, 150U).s32();
200 CONSTANT(5U, 0U).s64();
201 CONSTANT(6U, 1.2F).f32();
202
203 BASIC_BLOCK(2U, -1L)
204 {
205 INST(25U, Opcode::DivI).s32().Inputs(0U).Imm(0xcU);
206 INST(9U, Opcode::Div).u32().Inputs(0U, 4U);
207 INST(10U, Opcode::Div).u64().Inputs(1U, 5U);
208 INST(11U, Opcode::Div).f32().Inputs(2U, 6U);
209 INST(26U, Opcode::DivI).s32().Inputs(0U).Imm(static_cast<uint64_t>(-1L));
210 INST(27U, Opcode::ModI).s32().Inputs(0U).Imm(0xcU);
211 INST(14U, Opcode::Mod).u32().Inputs(0U, 4U);
212 INST(15U, Opcode::Mod).u64().Inputs(1U, 5U);
213 INST(16U, Opcode::Mod).f32().Inputs(2U, 6U);
214 INST(28U, Opcode::ModI).s32().Inputs(0U).Imm(static_cast<uint64_t>(-1L));
215 INST(29U, Opcode::MulI).s32().Inputs(0U).Imm(0xcU);
216 INST(19U, Opcode::Mul).u32().Inputs(0U, 4U);
217 INST(20U, Opcode::Mul).u64().Inputs(1U, 5U);
218 INST(21U, Opcode::Mul).f32().Inputs(2U, 6U);
219 INST(30U, Opcode::MulI).s32().Inputs(0U).Imm(static_cast<uint64_t>(-1L));
220 INST(31U, Opcode::SaveState).NoVregs();
221 INST(23U, Opcode::CallStatic)
222 .b()
223 .InputsAutoType(25U, 9U, 10U, 11U, 26U, 27U, 14U, 15U, 16U, 28U, 29U, 19U, 20U, 21U, 30U, 31U);
224 INST(24U, Opcode::Return).b().Inputs(23U);
225 }
226 }
227 EXPECT_TRUE(GraphComparator().Compare(init, expected));
228 }
229
230 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST_F(LoweringTest,Logic)231 TEST_F(LoweringTest, Logic)
232 {
233 auto init = CreateEmptyGraph();
234 GRAPH(init)
235 {
236 PARAMETER(0U, 0U).u32();
237 PARAMETER(24U, 1U).s64();
238 CONSTANT(1U, 12U).s32();
239 CONSTANT(2U, 50U).s32();
240 CONSTANT(25U, 0U).s64();
241 CONSTANT(27U, 300U).s32();
242
243 BASIC_BLOCK(2U, -1L)
244 {
245 INST(3U, Opcode::Or).u32().Inputs(0U, 1U);
246 INST(5U, Opcode::Or).u32().Inputs(0U, 2U);
247 INST(6U, Opcode::And).u32().Inputs(0U, 1U);
248 INST(8U, Opcode::And).u32().Inputs(0U, 2U);
249 INST(9U, Opcode::Xor).u32().Inputs(0U, 1U);
250 INST(11U, Opcode::Xor).u32().Inputs(0U, 2U);
251 INST(12U, Opcode::Or).u8().Inputs(0U, 1U);
252 INST(13U, Opcode::And).u32().Inputs(0U, 0U);
253 INST(26U, Opcode::And).s64().Inputs(24U, 25U);
254 INST(28U, Opcode::Xor).u32().Inputs(0U, 27U);
255 INST(29U, Opcode::Or).s64().Inputs(24U, 25U);
256 INST(30U, Opcode::Xor).s64().Inputs(24U, 25U);
257 INST(31U, Opcode::And).u32().Inputs(0U, 27U);
258 INST(32U, Opcode::Or).u32().Inputs(0U, 27U);
259 INST(15U, Opcode::SaveState).NoVregs();
260 INST(14U, Opcode::CallStatic)
261 .b()
262 .InputsAutoType(3U, 5U, 6U, 8U, 9U, 11U, 12U, 13U, 26U, 28U, 29U, 30U, 31U, 32U, 15U);
263 INST(23U, Opcode::Return).b().Inputs(14U);
264 }
265 }
266
267 #ifndef NDEBUG
268 init->SetLowLevelInstructionsEnabled();
269 #endif
270 init->RunPass<compiler::Lowering>();
271 init->RunPass<compiler::Cleanup>();
272
273 auto expected = CreateEmptyGraph();
274 GRAPH(expected)
275 {
276 PARAMETER(0U, 0U).u32();
277 PARAMETER(24U, 1U).s64();
278 CONSTANT(1U, 12U).s32();
279 CONSTANT(25U, 0U).s64();
280
281 BASIC_BLOCK(2U, -1L)
282 {
283 INST(33U, Opcode::OrI).u32().Inputs(0U).Imm(0xcU);
284 INST(34U, Opcode::OrI).u32().Inputs(0U).Imm(0x32U);
285 INST(35U, Opcode::AndI).u32().Inputs(0U).Imm(0xcU);
286 INST(36U, Opcode::AndI).u32().Inputs(0U).Imm(0x32U);
287 INST(37U, Opcode::XorI).u32().Inputs(0U).Imm(0xcU);
288 INST(38U, Opcode::XorI).u32().Inputs(0U).Imm(0x32U);
289 INST(12U, Opcode::Or).u8().Inputs(0U, 1U);
290 INST(13U, Opcode::And).u32().Inputs(0U, 0U);
291 INST(26U, Opcode::And).s64().Inputs(24U, 25U);
292 INST(39U, Opcode::XorI).u32().Inputs(0U).Imm(0x12cU);
293 INST(29U, Opcode::Or).s64().Inputs(24U, 25U);
294 INST(30U, Opcode::Xor).s64().Inputs(24U, 25U);
295 INST(40U, Opcode::AndI).u32().Inputs(0U).Imm(0x12cU);
296 INST(41U, Opcode::OrI).u32().Inputs(0U).Imm(0x12cU);
297 INST(15U, Opcode::SaveState).NoVregs();
298 INST(14U, Opcode::CallStatic)
299 .b()
300 .InputsAutoType(33U, 34U, 35U, 36U, 37U, 38U, 12U, 13U, 26U, 39U, 29U, 30U, 40U, 41U, 15U);
301 INST(23U, Opcode::Return).b().Inputs(14U);
302 }
303 }
304 EXPECT_TRUE(GraphComparator().Compare(init, expected));
305 }
306
307 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST_F(LoweringTest,Shift)308 TEST_F(LoweringTest, Shift)
309 {
310 auto init = CreateEmptyGraph();
311 GRAPH(init)
312 {
313 PARAMETER(0U, 0U).u32();
314 PARAMETER(24U, 1U).s64();
315 CONSTANT(1U, 12U).s32();
316 CONSTANT(2U, 64U).s32();
317 CONSTANT(25U, 0U).s64();
318 CONSTANT(27U, 200U).s32();
319
320 BASIC_BLOCK(2U, -1L)
321 {
322 INST(3U, Opcode::Shr).u32().Inputs(0U, 1U);
323 INST(5U, Opcode::Shr).u32().Inputs(0U, 2U);
324 INST(6U, Opcode::AShr).u32().Inputs(0U, 1U);
325 INST(8U, Opcode::AShr).u32().Inputs(0U, 2U);
326 INST(9U, Opcode::Shl).u32().Inputs(0U, 1U);
327 INST(11U, Opcode::Shl).u32().Inputs(0U, 2U);
328 INST(12U, Opcode::Shl).u8().Inputs(0U, 1U);
329 INST(13U, Opcode::Shr).u32().Inputs(0U, 0U);
330 INST(26U, Opcode::Shr).s64().Inputs(24U, 25U);
331 INST(28U, Opcode::AShr).s32().Inputs(0U, 27U);
332 INST(29U, Opcode::AShr).s64().Inputs(24U, 25U);
333 INST(30U, Opcode::Shl).s64().Inputs(24U, 25U);
334 INST(31U, Opcode::Shr).s32().Inputs(0U, 27U);
335 INST(32U, Opcode::Shl).s32().Inputs(0U, 27U);
336 INST(15U, Opcode::SaveState).NoVregs();
337 INST(14U, Opcode::CallStatic)
338 .b()
339 .InputsAutoType(3U, 5U, 6U, 8U, 9U, 11U, 12U, 13U, 26U, 28U, 29U, 30U, 31U, 32U, 15U);
340 INST(23U, Opcode::Return).b().Inputs(14U);
341 }
342 }
343 #ifndef NDEBUG
344 init->SetLowLevelInstructionsEnabled();
345 #endif
346 init->RunPass<compiler::Lowering>();
347 init->RunPass<compiler::Cleanup>();
348
349 auto expected = CreateEmptyGraph();
350 GRAPH(expected)
351 {
352 PARAMETER(0U, 0U).u32();
353 PARAMETER(24U, 1U).s64();
354 CONSTANT(1U, 12U).s32();
355 CONSTANT(2U, 64U).s32();
356 CONSTANT(25U, 0U).s64();
357 CONSTANT(27U, 200U).s32();
358
359 BASIC_BLOCK(2U, -1L)
360 {
361 INST(33U, Opcode::ShrI).u32().Inputs(0U).Imm(0xcU);
362 INST(5U, Opcode::Shr).u32().Inputs(0U, 2U);
363 INST(34U, Opcode::AShrI).u32().Inputs(0U).Imm(0xcU);
364 INST(8U, Opcode::AShr).u32().Inputs(0U, 2U);
365 INST(35U, Opcode::ShlI).u32().Inputs(0U).Imm(0xcU);
366 INST(11U, Opcode::Shl).u32().Inputs(0U, 2U);
367 INST(12U, Opcode::Shl).u8().Inputs(0U, 1U);
368 INST(13U, Opcode::Shr).u32().Inputs(0U, 0U);
369 INST(26U, Opcode::Shr).s64().Inputs(24U, 25U);
370 INST(28U, Opcode::AShr).s32().Inputs(0U, 27U);
371 INST(29U, Opcode::AShr).s64().Inputs(24U, 25U);
372 INST(30U, Opcode::Shl).s64().Inputs(24U, 25U);
373 INST(31U, Opcode::Shr).s32().Inputs(0U, 27U);
374 INST(32U, Opcode::Shl).s32().Inputs(0U, 27U);
375 INST(15U, Opcode::SaveState).NoVregs();
376 INST(14U, Opcode::CallStatic)
377 .b()
378 .InputsAutoType(33U, 5U, 34U, 8U, 35U, 11U, 12U, 13U, 26U, 28U, 29U, 30U, 31U, 32U, 15U);
379 INST(23U, Opcode::Return).b().Inputs(14U);
380 }
381 }
382 EXPECT_TRUE(GraphComparator().Compare(init, expected));
383 }
384
385 // NOLINTEND(readability-magic-numbers)
386
387 } // namespace ark::bytecodeopt::test
388