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
TEST_F(LoweringTest,AddSub)83 TEST_F(LoweringTest, AddSub)
84 {
85 auto init = CreateEmptyGraph();
86 GRAPH(init)
87 {
88 PARAMETER(0U, 0U).u32();
89 PARAMETER(1U, 1U).u64();
90 PARAMETER(2U, 2U).f32();
91 CONSTANT(3U, 12U).s32();
92 CONSTANT(4U, 150U).s32();
93 CONSTANT(5U, 0U).s64();
94 CONSTANT(6U, 1.2F).f32();
95 CONSTANT(7U, -1L).s32();
96
97 BASIC_BLOCK(2U, -1L)
98 {
99 INST(8U, Opcode::Add).u32().Inputs(0U, 3U);
100 INST(9U, Opcode::Sub).u32().Inputs(0U, 3U);
101 INST(10U, Opcode::Add).u32().Inputs(0U, 4U);
102 INST(11U, Opcode::Sub).u32().Inputs(0U, 4U);
103 INST(12U, Opcode::Add).u64().Inputs(1U, 5U);
104 INST(13U, Opcode::Sub).f32().Inputs(2U, 6U);
105 INST(14U, Opcode::Sub).u32().Inputs(0U, 7U);
106 INST(15U, Opcode::SaveState).NoVregs();
107 INST(20U, Opcode::CallStatic).b().InputsAutoType(8U, 9U, 10U, 11U, 12U, 13U, 14U, 15U);
108 INST(21U, Opcode::Return).b().Inputs(20U);
109 }
110 }
111 #ifndef NDEBUG
112 init->SetLowLevelInstructionsEnabled();
113 #endif
114 init->RunPass<compiler::Lowering>();
115 init->RunPass<compiler::Cleanup>();
116
117 auto expected = CreateEmptyGraph();
118 GRAPH(expected)
119 {
120 PARAMETER(0U, 0U).u32();
121 PARAMETER(1U, 1U).u64();
122 PARAMETER(2U, 2U).f32();
123 CONSTANT(4U, 150U).s32();
124 CONSTANT(5U, 0U).s64();
125 CONSTANT(6U, 1.2F).f32();
126
127 BASIC_BLOCK(2U, -1L)
128 {
129 INST(22U, Opcode::AddI).u32().Inputs(0U).Imm(0xcU);
130 INST(23U, Opcode::SubI).u32().Inputs(0U).Imm(0xcU);
131 INST(10U, Opcode::Add).u32().Inputs(0U, 4U);
132 INST(11U, Opcode::Sub).u32().Inputs(0U, 4U);
133 INST(12U, Opcode::Add).u64().Inputs(1U, 5U);
134 INST(13U, Opcode::Sub).f32().Inputs(2U, 6U);
135 INST(24U, Opcode::AddI).u32().Inputs(0U).Imm(1U);
136 INST(19U, Opcode::SaveState).NoVregs();
137 INST(20U, Opcode::CallStatic).b().InputsAutoType(22U, 23U, 10U, 11U, 12U, 13U, 24U, 19U);
138 INST(21U, Opcode::Return).b().Inputs(20U);
139 }
140 }
141 EXPECT_TRUE(GraphComparator().Compare(init, expected));
142 }
143
TEST_F(LoweringTest,MulDivMod)144 TEST_F(LoweringTest, MulDivMod)
145 {
146 auto init = CreateEmptyGraph();
147 GRAPH(init)
148 {
149 PARAMETER(0U, 0U).u32();
150 PARAMETER(1U, 1U).u64();
151 PARAMETER(2U, 2U).f32();
152 CONSTANT(3U, 12U).s32();
153 CONSTANT(4U, 150U).s32();
154 CONSTANT(5U, 0U).s64();
155 CONSTANT(6U, 1.2F).f32();
156 CONSTANT(7U, -1L).s32();
157
158 BASIC_BLOCK(2U, -1L)
159 {
160 INST(8U, Opcode::Div).s32().Inputs(0U, 3U);
161 INST(9U, Opcode::Div).u32().Inputs(0U, 4U);
162 INST(10U, Opcode::Div).u64().Inputs(1U, 5U);
163 INST(11U, Opcode::Div).f32().Inputs(2U, 6U);
164 INST(12U, Opcode::Div).s32().Inputs(0U, 7U);
165
166 INST(13U, Opcode::Mod).s32().Inputs(0U, 3U);
167 INST(14U, Opcode::Mod).u32().Inputs(0U, 4U);
168 INST(15U, Opcode::Mod).u64().Inputs(1U, 5U);
169 INST(16U, Opcode::Mod).f32().Inputs(2U, 6U);
170 INST(17U, Opcode::Mod).s32().Inputs(0U, 7U);
171
172 INST(18U, Opcode::Mul).s32().Inputs(0U, 3U);
173 INST(19U, Opcode::Mul).u32().Inputs(0U, 4U);
174 INST(20U, Opcode::Mul).u64().Inputs(1U, 5U);
175 INST(21U, Opcode::Mul).f32().Inputs(2U, 6U);
176 INST(22U, Opcode::Mul).s32().Inputs(0U, 7U);
177 INST(31U, Opcode::SaveState).NoVregs();
178 INST(23U, Opcode::CallStatic)
179 .b()
180 .InputsAutoType(8U, 9U, 10U, 11U, 12U, 13U, 14U, 15U, 16U, 17U, 18U, 19U, 20U, 21U, 22U, 31U);
181 INST(24U, Opcode::Return).b().Inputs(23U);
182 }
183 }
184
185 #ifndef NDEBUG
186 init->SetLowLevelInstructionsEnabled();
187 #endif
188 init->RunPass<compiler::Lowering>();
189 init->RunPass<compiler::Cleanup>();
190
191 auto expected = CreateEmptyGraph();
192 GRAPH(expected)
193 {
194 PARAMETER(0U, 0U).u32();
195 PARAMETER(1U, 1U).u64();
196 PARAMETER(2U, 2U).f32();
197 CONSTANT(4U, 150U).s32();
198 CONSTANT(5U, 0U).s64();
199 CONSTANT(6U, 1.2F).f32();
200
201 BASIC_BLOCK(2U, -1L)
202 {
203 INST(25U, Opcode::DivI).s32().Inputs(0U).Imm(0xcU);
204 INST(9U, Opcode::Div).u32().Inputs(0U, 4U);
205 INST(10U, Opcode::Div).u64().Inputs(1U, 5U);
206 INST(11U, Opcode::Div).f32().Inputs(2U, 6U);
207 INST(26U, Opcode::DivI).s32().Inputs(0U).Imm(static_cast<uint64_t>(-1L));
208 INST(27U, Opcode::ModI).s32().Inputs(0U).Imm(0xcU);
209 INST(14U, Opcode::Mod).u32().Inputs(0U, 4U);
210 INST(15U, Opcode::Mod).u64().Inputs(1U, 5U);
211 INST(16U, Opcode::Mod).f32().Inputs(2U, 6U);
212 INST(28U, Opcode::ModI).s32().Inputs(0U).Imm(static_cast<uint64_t>(-1L));
213 INST(29U, Opcode::MulI).s32().Inputs(0U).Imm(0xcU);
214 INST(19U, Opcode::Mul).u32().Inputs(0U, 4U);
215 INST(20U, Opcode::Mul).u64().Inputs(1U, 5U);
216 INST(21U, Opcode::Mul).f32().Inputs(2U, 6U);
217 INST(30U, Opcode::MulI).s32().Inputs(0U).Imm(static_cast<uint64_t>(-1L));
218 INST(31U, Opcode::SaveState).NoVregs();
219 INST(23U, Opcode::CallStatic)
220 .b()
221 .InputsAutoType(25U, 9U, 10U, 11U, 26U, 27U, 14U, 15U, 16U, 28U, 29U, 19U, 20U, 21U, 30U, 31U);
222 INST(24U, Opcode::Return).b().Inputs(23U);
223 }
224 }
225 EXPECT_TRUE(GraphComparator().Compare(init, expected));
226 }
227
TEST_F(LoweringTest,Logic)228 TEST_F(LoweringTest, Logic)
229 {
230 auto init = CreateEmptyGraph();
231 GRAPH(init)
232 {
233 PARAMETER(0U, 0U).u32();
234 PARAMETER(24U, 1U).s64();
235 CONSTANT(1U, 12U).s32();
236 CONSTANT(2U, 50U).s32();
237 CONSTANT(25U, 0U).s64();
238 CONSTANT(27U, 300U).s32();
239
240 BASIC_BLOCK(2U, -1L)
241 {
242 INST(3U, Opcode::Or).u32().Inputs(0U, 1U);
243 INST(5U, Opcode::Or).u32().Inputs(0U, 2U);
244 INST(6U, Opcode::And).u32().Inputs(0U, 1U);
245 INST(8U, Opcode::And).u32().Inputs(0U, 2U);
246 INST(9U, Opcode::Xor).u32().Inputs(0U, 1U);
247 INST(11U, Opcode::Xor).u32().Inputs(0U, 2U);
248 INST(12U, Opcode::Or).u8().Inputs(0U, 1U);
249 INST(13U, Opcode::And).u32().Inputs(0U, 0U);
250 INST(26U, Opcode::And).s64().Inputs(24U, 25U);
251 INST(28U, Opcode::Xor).u32().Inputs(0U, 27U);
252 INST(29U, Opcode::Or).s64().Inputs(24U, 25U);
253 INST(30U, Opcode::Xor).s64().Inputs(24U, 25U);
254 INST(31U, Opcode::And).u32().Inputs(0U, 27U);
255 INST(32U, Opcode::Or).u32().Inputs(0U, 27U);
256 INST(15U, Opcode::SaveState).NoVregs();
257 INST(14U, Opcode::CallStatic)
258 .b()
259 .InputsAutoType(3U, 5U, 6U, 8U, 9U, 11U, 12U, 13U, 26U, 28U, 29U, 30U, 31U, 32U, 15U);
260 INST(23U, Opcode::Return).b().Inputs(14U);
261 }
262 }
263
264 #ifndef NDEBUG
265 init->SetLowLevelInstructionsEnabled();
266 #endif
267 init->RunPass<compiler::Lowering>();
268 init->RunPass<compiler::Cleanup>();
269
270 auto expected = CreateEmptyGraph();
271 GRAPH(expected)
272 {
273 PARAMETER(0U, 0U).u32();
274 PARAMETER(24U, 1U).s64();
275 CONSTANT(1U, 12U).s32();
276 CONSTANT(25U, 0U).s64();
277
278 BASIC_BLOCK(2U, -1L)
279 {
280 INST(33U, Opcode::OrI).u32().Inputs(0U).Imm(0xcU);
281 INST(34U, Opcode::OrI).u32().Inputs(0U).Imm(0x32U);
282 INST(35U, Opcode::AndI).u32().Inputs(0U).Imm(0xcU);
283 INST(36U, Opcode::AndI).u32().Inputs(0U).Imm(0x32U);
284 INST(37U, Opcode::XorI).u32().Inputs(0U).Imm(0xcU);
285 INST(38U, Opcode::XorI).u32().Inputs(0U).Imm(0x32U);
286 INST(12U, Opcode::Or).u8().Inputs(0U, 1U);
287 INST(13U, Opcode::And).u32().Inputs(0U, 0U);
288 INST(26U, Opcode::And).s64().Inputs(24U, 25U);
289 INST(39U, Opcode::XorI).u32().Inputs(0U).Imm(0x12cU);
290 INST(29U, Opcode::Or).s64().Inputs(24U, 25U);
291 INST(30U, Opcode::Xor).s64().Inputs(24U, 25U);
292 INST(40U, Opcode::AndI).u32().Inputs(0U).Imm(0x12cU);
293 INST(41U, Opcode::OrI).u32().Inputs(0U).Imm(0x12cU);
294 INST(15U, Opcode::SaveState).NoVregs();
295 INST(14U, Opcode::CallStatic)
296 .b()
297 .InputsAutoType(33U, 34U, 35U, 36U, 37U, 38U, 12U, 13U, 26U, 39U, 29U, 30U, 40U, 41U, 15U);
298 INST(23U, Opcode::Return).b().Inputs(14U);
299 }
300 }
301 EXPECT_TRUE(GraphComparator().Compare(init, expected));
302 }
303
TEST_F(LoweringTest,Shift)304 TEST_F(LoweringTest, Shift)
305 {
306 auto init = CreateEmptyGraph();
307 GRAPH(init)
308 {
309 PARAMETER(0U, 0U).u32();
310 PARAMETER(24U, 1U).s64();
311 CONSTANT(1U, 12U).s32();
312 CONSTANT(2U, 64U).s32();
313 CONSTANT(25U, 0U).s64();
314 CONSTANT(27U, 200U).s32();
315
316 BASIC_BLOCK(2U, -1L)
317 {
318 INST(3U, Opcode::Shr).u32().Inputs(0U, 1U);
319 INST(5U, Opcode::Shr).u32().Inputs(0U, 2U);
320 INST(6U, Opcode::AShr).u32().Inputs(0U, 1U);
321 INST(8U, Opcode::AShr).u32().Inputs(0U, 2U);
322 INST(9U, Opcode::Shl).u32().Inputs(0U, 1U);
323 INST(11U, Opcode::Shl).u32().Inputs(0U, 2U);
324 INST(12U, Opcode::Shl).u8().Inputs(0U, 1U);
325 INST(13U, Opcode::Shr).u32().Inputs(0U, 0U);
326 INST(26U, Opcode::Shr).s64().Inputs(24U, 25U);
327 INST(28U, Opcode::AShr).s32().Inputs(0U, 27U);
328 INST(29U, Opcode::AShr).s64().Inputs(24U, 25U);
329 INST(30U, Opcode::Shl).s64().Inputs(24U, 25U);
330 INST(31U, Opcode::Shr).s32().Inputs(0U, 27U);
331 INST(32U, Opcode::Shl).s32().Inputs(0U, 27U);
332 INST(15U, Opcode::SaveState).NoVregs();
333 INST(14U, Opcode::CallStatic)
334 .b()
335 .InputsAutoType(3U, 5U, 6U, 8U, 9U, 11U, 12U, 13U, 26U, 28U, 29U, 30U, 31U, 32U, 15U);
336 INST(23U, Opcode::Return).b().Inputs(14U);
337 }
338 }
339 #ifndef NDEBUG
340 init->SetLowLevelInstructionsEnabled();
341 #endif
342 init->RunPass<compiler::Lowering>();
343 init->RunPass<compiler::Cleanup>();
344
345 auto expected = CreateEmptyGraph();
346 GRAPH(expected)
347 {
348 PARAMETER(0U, 0U).u32();
349 PARAMETER(24U, 1U).s64();
350 CONSTANT(1U, 12U).s32();
351 CONSTANT(2U, 64U).s32();
352 CONSTANT(25U, 0U).s64();
353 CONSTANT(27U, 200U).s32();
354
355 BASIC_BLOCK(2U, -1L)
356 {
357 INST(33U, Opcode::ShrI).u32().Inputs(0U).Imm(0xcU);
358 INST(5U, Opcode::Shr).u32().Inputs(0U, 2U);
359 INST(34U, Opcode::AShrI).u32().Inputs(0U).Imm(0xcU);
360 INST(8U, Opcode::AShr).u32().Inputs(0U, 2U);
361 INST(35U, Opcode::ShlI).u32().Inputs(0U).Imm(0xcU);
362 INST(11U, Opcode::Shl).u32().Inputs(0U, 2U);
363 INST(12U, Opcode::Shl).u8().Inputs(0U, 1U);
364 INST(13U, Opcode::Shr).u32().Inputs(0U, 0U);
365 INST(26U, Opcode::Shr).s64().Inputs(24U, 25U);
366 INST(28U, Opcode::AShr).s32().Inputs(0U, 27U);
367 INST(29U, Opcode::AShr).s64().Inputs(24U, 25U);
368 INST(30U, Opcode::Shl).s64().Inputs(24U, 25U);
369 INST(31U, Opcode::Shr).s32().Inputs(0U, 27U);
370 INST(32U, Opcode::Shl).s32().Inputs(0U, 27U);
371 INST(15U, Opcode::SaveState).NoVregs();
372 INST(14U, Opcode::CallStatic)
373 .b()
374 .InputsAutoType(33U, 5U, 34U, 8U, 35U, 11U, 12U, 13U, 26U, 28U, 29U, 30U, 31U, 32U, 15U);
375 INST(23U, Opcode::Return).b().Inputs(14U);
376 }
377 }
378 EXPECT_TRUE(GraphComparator().Compare(init, expected));
379 }
380
381 // NOLINTEND(readability-magic-numbers)
382
383 } // namespace ark::bytecodeopt::test
384