1 /**
2 * Copyright (c) 2021-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 "bytecode_emitter.h"
17 #include <gtest/gtest.h>
18
19 #include <array>
20 #include <functional>
21 #include <limits>
22 #include <tuple>
23 #include <vector>
24
25 namespace ark::panda_file::test {
26
27 using Opcode = BytecodeInstruction::Opcode;
28
29 using Tuple16 = std::tuple<uint8_t, uint8_t>;
30 using Tuple32 = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t>;
31 using Tuple64 = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>;
32
33 // NOLINTBEGIN(readability-magic-numbers,hicpp-signed-bitwise)
34
35 namespace globals {
36 constexpr static const int64_t IMM_2 = 2;
37 constexpr static const int64_t IMM_3 = 3;
38 constexpr static const int64_t IMM_4 = 4;
39 constexpr static const int64_t IMM_5 = 5;
40 constexpr static const int64_t IMM_6 = 6;
41 constexpr static const int64_t IMM_7 = 7;
42 constexpr static const int64_t IMM_8 = 8;
43 constexpr static const int64_t IMM_9 = 9;
44 constexpr static const int64_t IMM_11 = 11;
45 constexpr static const int64_t IMM_12 = 12;
46 constexpr static const int64_t IMM_15 = 15;
47 constexpr static const int64_t IMM_16 = 16;
48 constexpr static const int64_t IMM_24 = 24;
49 constexpr static const int64_t IMM_32 = 32;
50 constexpr static const int64_t IMM_40 = 40;
51 constexpr static const int64_t IMM_48 = 48;
52 constexpr static const int64_t IMM_56 = 56;
53 } // namespace globals
54
operator <<(std::vector<uint8_t> & out,uint8_t val)55 static std::vector<uint8_t> &operator<<(std::vector<uint8_t> &out, uint8_t val)
56 {
57 out.push_back(val);
58 return out;
59 }
60
61 static std::vector<uint8_t> &operator<<(std::vector<uint8_t> &out, Opcode op);
62
operator <<(std::vector<uint8_t> & out,Tuple16 val)63 static std::vector<uint8_t> &operator<<(std::vector<uint8_t> &out, Tuple16 val)
64 {
65 return out << std::get<0>(val) << std::get<1>(val);
66 }
67
operator <<(std::vector<uint8_t> & out,Tuple32 val)68 static std::vector<uint8_t> &operator<<(std::vector<uint8_t> &out, Tuple32 val)
69 {
70 return out << std::get<0>(val) << std::get<1>(val) << std::get<globals::IMM_2>(val)
71 << std::get<globals::IMM_3>(val);
72 }
73
operator <<(std::vector<uint8_t> & out,Tuple64 val)74 static std::vector<uint8_t> &operator<<(std::vector<uint8_t> &out, Tuple64 val)
75 {
76 return out << std::get<0>(val) << std::get<1>(val) << std::get<globals::IMM_2>(val) << std::get<globals::IMM_3>(val)
77 << std::get<globals::IMM_4>(val) << std::get<globals::IMM_5>(val) << std::get<globals::IMM_6>(val)
78 << std::get<globals::IMM_7>(val);
79 }
80
Split16(uint16_t val)81 static Tuple16 Split16(uint16_t val)
82 {
83 return Tuple16 {val & 0xFF, val >> 8};
84 }
85
Split32(uint32_t val)86 static Tuple32 Split32(uint32_t val)
87 {
88 return Tuple32 {val & 0xFF, (val >> globals::IMM_8) & 0xFF, (val >> globals::IMM_16) & 0xFF,
89 (val >> globals::IMM_24) & 0xFF};
90 }
91
Split64(uint64_t val)92 static Tuple64 Split64(uint64_t val)
93 {
94 return Tuple64 {val & 0xFF,
95 (val >> globals::IMM_8) & 0xFF,
96 (val >> globals::IMM_16) & 0xFF,
97 (val >> globals::IMM_24) & 0xFF,
98 (val >> globals::IMM_32) & 0xFF,
99 (val >> globals::IMM_40) & 0xFF,
100 (val >> globals::IMM_48) & 0xFF,
101 (val >> globals::IMM_56) & 0xFF};
102 }
103
TEST(BytecodeEmitter,JmpBwd_IMM8)104 TEST(BytecodeEmitter, JmpBwd_IMM8)
105 {
106 BytecodeEmitter emitter;
107 Label label = emitter.CreateLabel();
108 emitter.Bind(label);
109 int numRet = -std::numeric_limits<int8_t>::min();
110 for (int i = 0; i < numRet; ++i) {
111 emitter.ReturnVoid();
112 }
113 emitter.Jmp(label);
114 std::vector<uint8_t> out;
115 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
116 std::vector<uint8_t> expected;
117 for (int i = 0; i < numRet; ++i) {
118 expected << Opcode::RETURN_VOID;
119 }
120 expected << Opcode::JMP_IMM8 << -numRet;
121 ASSERT_EQ(expected, out);
122 }
123
TEST(BytecodeEmitter,JmpFwd_IMM8)124 TEST(BytecodeEmitter, JmpFwd_IMM8)
125 {
126 BytecodeEmitter emitter;
127 Label label = emitter.CreateLabel();
128 emitter.Jmp(label);
129 // -5 because 2 bytes takes jmp itself and
130 // emitter estimate length of jmp by 3 greater the it is actually
131 int numRet = std::numeric_limits<int8_t>::max() - globals::IMM_5;
132 for (int i = 0; i < numRet; ++i) {
133 emitter.ReturnVoid();
134 }
135 emitter.Bind(label);
136 emitter.ReturnVoid();
137 std::vector<uint8_t> out;
138 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
139 std::vector<uint8_t> expected;
140 expected << Opcode::JMP_IMM8 << numRet + globals::IMM_2;
141 for (int i = 0; i < numRet + 1; ++i) {
142 expected << Opcode::RETURN_VOID;
143 }
144 ASSERT_EQ(expected, out);
145 }
146
TEST(BytecodeEmitter,JmpBwd_IMM16)147 TEST(BytecodeEmitter, JmpBwd_IMM16)
148 {
149 {
150 BytecodeEmitter emitter;
151 Label label = emitter.CreateLabel();
152 emitter.Bind(label);
153 int numRet = -std::numeric_limits<int8_t>::min() + 1;
154 for (int i = 0; i < numRet; ++i) {
155 emitter.ReturnVoid();
156 }
157 emitter.Jmp(label);
158 std::vector<uint8_t> out;
159 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
160 std::vector<uint8_t> expected;
161 for (int i = 0; i < numRet; ++i) {
162 expected << Opcode::RETURN_VOID;
163 }
164 expected << Opcode::JMP_IMM16 << Split16(-numRet);
165 ASSERT_EQ(expected, out);
166 }
167 {
168 BytecodeEmitter emitter;
169 Label label = emitter.CreateLabel();
170 emitter.Bind(label);
171 int numRet = -std::numeric_limits<int16_t>::min();
172 for (int i = 0; i < numRet; ++i) {
173 emitter.ReturnVoid();
174 }
175 emitter.Jmp(label);
176 std::vector<uint8_t> out;
177 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
178 std::vector<uint8_t> expected;
179 for (int i = 0; i < numRet; ++i) {
180 expected << Opcode::RETURN_VOID;
181 }
182 expected << Opcode::JMP_IMM16 << Split16(-numRet);
183 ASSERT_EQ(expected, out);
184 }
185 }
186
TEST(BytecodeEmitter,JmpFwd_IMM16)187 TEST(BytecodeEmitter, JmpFwd_IMM16)
188 {
189 {
190 BytecodeEmitter emitter;
191 Label label = emitter.CreateLabel();
192 emitter.Jmp(label);
193 // -4 because 2 bytes takes jmp itself and
194 // emitter estimate length of jmp by 3 greater the it is actually
195 // and plus one byte to make 8bit overflow
196 int numRet = std::numeric_limits<int8_t>::max() - globals::IMM_4;
197 for (int i = 0; i < numRet; ++i) {
198 emitter.ReturnVoid();
199 }
200 emitter.Bind(label);
201 emitter.ReturnVoid();
202 std::vector<uint8_t> out;
203 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
204 std::vector<uint8_t> expected;
205 expected << Opcode::JMP_IMM16 << Split16(numRet + globals::IMM_3);
206 for (int i = 0; i < numRet + 1; ++i) {
207 expected << Opcode::RETURN_VOID;
208 }
209 ASSERT_EQ(expected, out);
210 }
211 {
212 BytecodeEmitter emitter;
213 Label label = emitter.CreateLabel();
214 emitter.Jmp(label);
215 // -5 because 2 bytes takes jmp itself and
216 // emitter estimate length of jmp by 3 greater the it is actually
217 int numRet = std::numeric_limits<int16_t>::max() - globals::IMM_5;
218 for (int i = 0; i < numRet; ++i) {
219 emitter.ReturnVoid();
220 }
221 emitter.Bind(label);
222 emitter.ReturnVoid();
223 std::vector<uint8_t> out;
224 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
225 std::vector<uint8_t> expected;
226 expected << Opcode::JMP_IMM16 << Split16(numRet + globals::IMM_3);
227 for (int i = 0; i < numRet + 1; ++i) {
228 expected << Opcode::RETURN_VOID;
229 }
230 ASSERT_EQ(expected, out);
231 }
232 }
233
EmitJmp(Opcode op,int32_t imm,std::vector<uint8_t> * out)234 static void EmitJmp(Opcode op, int32_t imm, std::vector<uint8_t> *out)
235 {
236 *out << op;
237 switch (op) {
238 case Opcode::JMP_IMM8:
239 *out << static_cast<int8_t>(imm);
240 break;
241 case Opcode::JMP_IMM16:
242 *out << Split16(imm);
243 break;
244 default:
245 *out << Split32(imm);
246 break;
247 }
248 }
249
GetOpcode(size_t instSize)250 static Opcode GetOpcode(size_t instSize)
251 {
252 switch (instSize) {
253 case globals::IMM_2:
254 return Opcode::JMP_IMM8;
255 case globals::IMM_3:
256 return Opcode::JMP_IMM16;
257 default:
258 return Opcode::JMP_IMM32;
259 }
260 }
261
262 /*
263 * Emit bytecode for the following program:
264 *
265 * label1:
266 * jmp label2
267 * ... <- n1 return.void instructions
268 * jmp label1
269 * ... <- n2 return.void instructions
270 * label2:
271 * return.void
272 */
273 // CC-OFFNXT(huge_method[C++], huge_depth[C++]) solid logic
EmitJmpFwdBwd(size_t n1,size_t n2)274 static std::vector<uint8_t> EmitJmpFwdBwd(size_t n1, size_t n2)
275 {
276 std::array<std::tuple<size_t, int32_t, int32_t>, 3U> jmps {
277 std::tuple {globals::IMM_2, std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()},
278 std::tuple {globals::IMM_3, std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max()},
279 std::tuple {globals::IMM_5, std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max()}};
280
281 std::vector<uint8_t> out;
282
283 size_t jmpSize1;
284 size_t jmpSize2;
285 int32_t immMax1;
286 int32_t immMin2;
287
288 for (const auto &t1 : jmps) {
289 std::tie(jmpSize1, std::ignore, immMax1) = t1;
290
291 for (const auto &t2 : jmps) {
292 std::tie(jmpSize2, immMin2, std::ignore) = t2;
293
294 int32_t imm1 = jmpSize1 + n1 + jmpSize2 + n2;
295 int32_t imm2 = jmpSize1 + n1;
296
297 if (imm1 <= immMax1 && -imm2 >= immMin2) {
298 EmitJmp(GetOpcode(jmpSize1), imm1, &out);
299
300 for (size_t i = 0; i < n1; i++) {
301 out << Opcode::RETURN_VOID;
302 }
303
304 EmitJmp(GetOpcode(jmpSize2), -imm2, &out);
305
306 for (size_t i = 0; i < n2; i++) {
307 out << Opcode::RETURN_VOID;
308 }
309
310 out << Opcode::RETURN_VOID;
311
312 return out;
313 }
314 }
315 }
316
317 return out;
318 }
319
320 /*
321 // CC-OFFNXT(G.CMT.04-CPP) false positive
322 * Test following control flow:
323 *
324 * label1:
325 * jmp label2
326 * ... <- n1 return.void instructions
327 * jmp label1
328 * ... <- n2 return.void instructions
329 * label2:
330 * return.void
331 */
TestJmpFwdBwd(size_t n1,size_t n2)332 void TestJmpFwdBwd(size_t n1, size_t n2)
333 {
334 BytecodeEmitter emitter;
335 Label label1 = emitter.CreateLabel();
336 Label label2 = emitter.CreateLabel();
337
338 emitter.Bind(label1);
339 emitter.Jmp(label2);
340 for (size_t i = 0; i < n1; ++i) {
341 emitter.ReturnVoid();
342 }
343 emitter.Jmp(label1);
344 for (size_t i = 0; i < n2; ++i) {
345 emitter.ReturnVoid();
346 }
347 emitter.Bind(label2);
348 emitter.ReturnVoid();
349
350 std::vector<uint8_t> out;
351 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)) << "n1 = " << n1 << " n2 = " << n2;
352
353 ASSERT_EQ(EmitJmpFwdBwd(n1, n2), out) << "n1 = " << n1 << " n2 = " << n2;
354 }
355
TEST(BytecodeEmitter,JmpFwdBwd)356 TEST(BytecodeEmitter, JmpFwdBwd)
357 {
358 // fwd jmp imm16
359 // bwd jmp imm8
360 TestJmpFwdBwd(0, std::numeric_limits<int8_t>::max());
361
362 // fwd jmp imm16
363 // bwd jmp imm16
364 TestJmpFwdBwd(std::numeric_limits<int8_t>::max(), 0);
365
366 // fwd jmp imm32
367 // bwd jmp imm8
368 TestJmpFwdBwd(0, std::numeric_limits<int16_t>::max());
369
370 // fwd jmp imm32
371 // bwd jmp imm16
372 TestJmpFwdBwd(std::numeric_limits<int8_t>::max(), std::numeric_limits<int16_t>::max());
373
374 // fwd jmp imm32
375 // bwd jmp imm32
376 TestJmpFwdBwd(std::numeric_limits<int16_t>::max(), 0);
377 }
378
TEST(BytecodeEmitter,JmpBwd_IMM32)379 TEST(BytecodeEmitter, JmpBwd_IMM32)
380 {
381 BytecodeEmitter emitter;
382 Label label = emitter.CreateLabel();
383 emitter.Bind(label);
384 int numRet = -std::numeric_limits<int16_t>::min() + 1;
385 for (int i = 0; i < numRet; ++i) {
386 emitter.ReturnVoid();
387 }
388 emitter.Jmp(label);
389 std::vector<uint8_t> out;
390 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
391 std::vector<uint8_t> expected;
392 for (int i = 0; i < numRet; ++i) {
393 expected << Opcode::RETURN_VOID;
394 }
395 expected << Opcode::JMP_IMM32 << Split32(-numRet);
396 ASSERT_EQ(expected, out);
397 }
398
TEST(BytecodeEmitter,JmpFwd_IMM32)399 TEST(BytecodeEmitter, JmpFwd_IMM32)
400 {
401 BytecodeEmitter emitter;
402 Label label = emitter.CreateLabel();
403 emitter.Jmp(label);
404 // -4 because 2 bytes takes jmp itself and
405 // emitter estimate length of jmp by 3 greater the it is actually
406 // and plus one byte to make 16bit overflow
407 int numRet = std::numeric_limits<int16_t>::max() - globals::IMM_4;
408 for (int i = 0; i < numRet; ++i) {
409 emitter.ReturnVoid();
410 }
411 emitter.Bind(label);
412 emitter.ReturnVoid();
413 std::vector<uint8_t> out;
414 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
415 std::vector<uint8_t> expected;
416 expected << Opcode::JMP_IMM32 << Split32(numRet + globals::IMM_5);
417 for (int i = 0; i < numRet + 1; ++i) {
418 expected << Opcode::RETURN_VOID;
419 }
420 ASSERT_EQ(expected, out);
421 }
422
423 // NOLINTNEXTLINE(readability-identifier-naming)
JcmpBwd_V8_IMM8(Opcode opcode,const std::function<void (BytecodeEmitter *,uint8_t,const Label & label)> & emitJcmp)424 static void JcmpBwd_V8_IMM8(Opcode opcode,
425 const std::function<void(BytecodeEmitter *, uint8_t, const Label &label)> &emitJcmp)
426 {
427 BytecodeEmitter emitter;
428 Label label = emitter.CreateLabel();
429 emitter.Bind(label);
430 int numRet = globals::IMM_15;
431 for (int i = 0; i < numRet; ++i) {
432 emitter.ReturnVoid();
433 }
434 emitJcmp(&emitter, globals::IMM_15, label);
435 std::vector<uint8_t> out;
436 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
437 std::vector<uint8_t> expected;
438 for (int i = 0; i < numRet; ++i) {
439 expected << Opcode::RETURN_VOID;
440 }
441
442 expected << opcode << 15U << static_cast<uint8_t>(-numRet);
443 ASSERT_EQ(expected, out);
444 }
445
446 // NOLINTNEXTLINE(readability-identifier-naming)
JcmpFwd_V8_IMM8(Opcode opcode,const std::function<void (BytecodeEmitter *,uint8_t,const Label & label)> & emitJcmp)447 static void JcmpFwd_V8_IMM8(Opcode opcode,
448 const std::function<void(BytecodeEmitter *, uint8_t, const Label &label)> &emitJcmp)
449 {
450 BytecodeEmitter emitter;
451 Label label = emitter.CreateLabel();
452 emitJcmp(&emitter, globals::IMM_15, label);
453 int numRet = globals::IMM_12;
454 for (int i = 0; i < numRet; ++i) {
455 emitter.ReturnVoid();
456 }
457 emitter.Bind(label);
458 emitter.ReturnVoid();
459 std::vector<uint8_t> out;
460 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
461 std::vector<uint8_t> expected;
462 // 2 bytes takes jmp itself and plus one byte to make.
463 expected << opcode << 15U << static_cast<uint8_t>(numRet + globals::IMM_3);
464
465 for (int i = 0; i < numRet + 1; ++i) {
466 expected << Opcode::RETURN_VOID;
467 }
468
469 ASSERT_EQ(expected, out);
470 }
471
472 // NOLINTNEXTLINE(readability-identifier-naming)
JcmpBwd_V8_IMM16(Opcode opcode,const std::function<void (BytecodeEmitter *,uint8_t,const Label & label)> & emitJcmp)473 static void JcmpBwd_V8_IMM16(Opcode opcode,
474 const std::function<void(BytecodeEmitter *, uint8_t, const Label &label)> &emitJcmp)
475 {
476 {
477 // Test min imm value
478 BytecodeEmitter emitter;
479 Label label = emitter.CreateLabel();
480 emitter.Bind(label);
481 int numRet = -std::numeric_limits<int8_t>::min();
482 ++numRet;
483 for (int i = 0; i < numRet; ++i) {
484 emitter.ReturnVoid();
485 }
486 emitJcmp(&emitter, 0, label);
487 std::vector<uint8_t> out;
488 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
489 std::vector<uint8_t> expected;
490 for (int i = 0; i < numRet; ++i) {
491 expected << Opcode::RETURN_VOID;
492 }
493 expected << opcode << 0U << Split16(-numRet);
494 ASSERT_EQ(expected, out);
495 }
496 {
497 // Test max imm value
498 BytecodeEmitter emitter;
499 Label label = emitter.CreateLabel();
500 emitter.Bind(label);
501 int numRet = -std::numeric_limits<int16_t>::min();
502 for (int i = 0; i < numRet; ++i) {
503 emitter.ReturnVoid();
504 }
505 emitJcmp(&emitter, 0, label);
506 std::vector<uint8_t> out;
507 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
508 std::vector<uint8_t> expected;
509 for (int i = 0; i < numRet; ++i) {
510 expected << Opcode::RETURN_VOID;
511 }
512 expected << opcode << 0U << Split16(-numRet);
513 ASSERT_EQ(expected, out);
514 }
515 }
516
517 // NOLINTNEXTLINE(readability-identifier-naming)
JcmpFwd_V8_IMM16(Opcode opcode,const std::function<void (BytecodeEmitter *,uint8_t,const Label & label)> & emitJcmp)518 static void JcmpFwd_V8_IMM16(Opcode opcode,
519 const std::function<void(BytecodeEmitter *, uint8_t, const Label &label)> &emitJcmp)
520 {
521 {
522 // Test min imm
523 BytecodeEmitter emitter;
524 Label label = emitter.CreateLabel();
525 emitJcmp(&emitter, 0, label);
526 // -3 because 4 bytes takes jmp itself
527 // plus one to make 8bit overflow
528 int numRet = std::numeric_limits<int8_t>::max() - globals::IMM_3;
529 for (int i = 0; i < numRet; ++i) {
530 emitter.ReturnVoid();
531 }
532 emitter.Bind(label);
533 emitter.ReturnVoid();
534 std::vector<uint8_t> out;
535 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
536 std::vector<uint8_t> expected;
537 expected << opcode << 0U << Split16(numRet + globals::IMM_4);
538 for (int i = 0; i < numRet + 1; ++i) {
539 expected << Opcode::RETURN_VOID;
540 }
541 ASSERT_EQ(expected, out);
542 }
543 {
544 // Test max imm
545 BytecodeEmitter emitter;
546 Label label = emitter.CreateLabel();
547 emitJcmp(&emitter, 0, label);
548 // -4 because 4 bytes takes jmp itself
549 int numRet = std::numeric_limits<int16_t>::max() - globals::IMM_4;
550 for (int i = 0; i < numRet; ++i) {
551 emitter.ReturnVoid();
552 }
553 emitter.Bind(label);
554 emitter.ReturnVoid();
555 std::vector<uint8_t> out;
556 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
557 std::vector<uint8_t> expected;
558 expected << opcode << 0U << Split16(numRet + globals::IMM_4);
559 for (int i = 0; i < numRet + 1; ++i) {
560 expected << Opcode::RETURN_VOID;
561 }
562 ASSERT_EQ(expected, out);
563 }
564 }
565
TEST(BytecodeEmitter,Jne_V8_IMM8)566 TEST(BytecodeEmitter, Jne_V8_IMM8)
567 {
568 BytecodeEmitter emitter;
569 Label label = emitter.CreateLabel();
570 emitter.Jne(0, label);
571 emitter.Bind(label);
572 emitter.ReturnVoid();
573 std::vector<uint8_t> out;
574 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
575 std::vector<uint8_t> expected;
576 expected << Opcode::JNE_V8_IMM8 << 0 << globals::IMM_3 << Opcode::RETURN_VOID;
577 ASSERT_EQ(expected, out);
578 }
579
TEST(BytecodeEmitter,Jne_V8_IMM16)580 TEST(BytecodeEmitter, Jne_V8_IMM16)
581 {
582 BytecodeEmitter emitter;
583 Label label = emitter.CreateLabel();
584 static constexpr uint8_t JNE_REG = 16;
585 static constexpr size_t N_NOPS = 256;
586 static constexpr size_t INSN_SZ = 4;
587 emitter.Jne(JNE_REG, label);
588 for (size_t i = 0; i < N_NOPS; ++i) {
589 emitter.Nop();
590 }
591 emitter.Bind(label);
592 emitter.ReturnVoid();
593 std::vector<uint8_t> out;
594 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
595 std::vector<uint8_t> expected;
596 expected << Opcode::JNE_V8_IMM16 << JNE_REG << Split16(INSN_SZ + N_NOPS);
597 for (size_t i = 0; i < N_NOPS; ++i) {
598 expected << Opcode::NOP;
599 }
600 expected << Opcode::RETURN_VOID;
601 ASSERT_EQ(expected, out);
602 }
603
604 // NOLINTNEXTLINE(readability-identifier-naming)
Jcmpz_IMM8(Opcode opcode,const std::function<void (BytecodeEmitter *,const Label & label)> & emit_jcmp)605 static void Jcmpz_IMM8(Opcode opcode, const std::function<void(BytecodeEmitter *, const Label &label)> &emit_jcmp)
606 {
607 BytecodeEmitter emitter;
608 Label label = emitter.CreateLabel();
609 emit_jcmp(&emitter, label);
610 emitter.Bind(label);
611 emitter.ReturnVoid();
612 std::vector<uint8_t> out;
613 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
614 std::vector<uint8_t> expected;
615 expected << opcode << globals::IMM_2 << Opcode::RETURN_VOID;
616 ASSERT_EQ(expected, out);
617 }
618
619 // NOLINTNEXTLINE(readability-identifier-naming)
Jcmpz_IMM16(Opcode opcode,const std::function<void (BytecodeEmitter *,const Label & label)> & emit_jcmp)620 static void Jcmpz_IMM16(Opcode opcode, const std::function<void(BytecodeEmitter *, const Label &label)> &emit_jcmp)
621 {
622 BytecodeEmitter emitter;
623 Label label = emitter.CreateLabel();
624 emit_jcmp(&emitter, label);
625 for (int i = 0; i < std::numeric_limits<uint8_t>::max() - globals::IMM_2; ++i) {
626 emitter.ReturnVoid();
627 }
628 emitter.Bind(label);
629 emitter.ReturnVoid();
630 std::vector<uint8_t> out;
631 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
632 std::vector<uint8_t> expected;
633 expected << opcode << Split16(std::numeric_limits<uint8_t>::max() + 1);
634 for (int i = 0; i < std::numeric_limits<uint8_t>::max() - 1; ++i) {
635 expected << Opcode::RETURN_VOID;
636 }
637 ASSERT_EQ(expected, out);
638 }
639
TEST(BytecodeEmitter,JmpFwdCrossRef)640 TEST(BytecodeEmitter, JmpFwdCrossRef)
641 {
642 // Situation:
643 // +---------+
644 // +----|----+ |
645 // | | | |
646 // | | v v
647 // ---*----*----*----*----
648 // lbl1 lbl2
649 BytecodeEmitter emitter;
650 Label lbl1 = emitter.CreateLabel();
651 Label lbl2 = emitter.CreateLabel();
652 emitter.Jeq(0, lbl1);
653 emitter.Jeq(0, lbl2);
654 emitter.ReturnVoid();
655 emitter.Bind(lbl1);
656 emitter.ReturnVoid();
657 for (int i = 0; i < globals::IMM_6; ++i) {
658 emitter.ReturnVoid();
659 }
660 emitter.Bind(lbl2);
661 emitter.ReturnVoid();
662 std::vector<uint8_t> out;
663 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
664
665 std::vector<uint8_t> expected;
666 expected << Opcode::JEQ_V8_IMM8 << 0U << globals::IMM_7 << Opcode::JEQ_V8_IMM8 << 0U << globals::IMM_11;
667 for (int i = 0; i < globals::IMM_9; ++i) {
668 expected << Opcode::RETURN_VOID;
669 }
670 ASSERT_EQ(expected, out);
671 }
672
TEST(BytecodeEmitter,JmpBwdCrossRef)673 TEST(BytecodeEmitter, JmpBwdCrossRef)
674 {
675 // Situation:
676 // +---------+
677 // | |
678 // +---------+ |
679 // | | | |
680 // v | | v
681 // ---*----*----*----*----
682 // lbl1 lbl2
683 BytecodeEmitter emitter;
684 Label lbl1 = emitter.CreateLabel();
685 Label lbl2 = emitter.CreateLabel();
686 emitter.Bind(lbl1);
687 emitter.ReturnVoid();
688 emitter.Jeq(0, lbl2);
689 for (int i = 0; i < globals::IMM_5; ++i) {
690 emitter.ReturnVoid();
691 }
692 emitter.Jeq(0, lbl1);
693 emitter.Bind(lbl2);
694 emitter.ReturnVoid();
695
696 std::vector<uint8_t> out;
697 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
698
699 std::vector<uint8_t> expected;
700 expected << Opcode::RETURN_VOID << Opcode::JEQ_V8_IMM8 << 0U << globals::IMM_11;
701 for (int i = 0; i < globals::IMM_5; ++i) {
702 expected << Opcode::RETURN_VOID;
703 }
704 expected << Opcode::JEQ_V8_IMM8 << 0U << static_cast<uint8_t>(-globals::IMM_9) << Opcode::RETURN_VOID;
705 ASSERT_EQ(expected, out);
706 }
707
TEST(BytecodeEmitter,Jmp3FwdCrossRefs)708 TEST(BytecodeEmitter, Jmp3FwdCrossRefs)
709 {
710 // Situation:
711 // +--------+
712 // +|--------+
713 // ||+-------+---+
714 // ||| v v
715 // ---***-------*---*
716 // lbl1 lbl2
717 BytecodeEmitter emitter;
718 Label lbl1 = emitter.CreateLabel();
719 Label lbl2 = emitter.CreateLabel();
720
721 emitter.Jmp(lbl1);
722 emitter.Jmp(lbl1);
723 emitter.Jmp(lbl2);
724
725 constexpr int32_t INT8T_MAX = std::numeric_limits<int8_t>::max();
726
727 size_t n = INT8T_MAX - globals::IMM_4;
728 for (size_t i = 0; i < n; i++) {
729 emitter.ReturnVoid();
730 }
731
732 emitter.Bind(lbl1);
733 emitter.ReturnVoid();
734 emitter.ReturnVoid();
735 emitter.Bind(lbl2);
736
737 std::vector<uint8_t> out;
738 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
739
740 std::vector<uint8_t> expected;
741 expected << Opcode::JMP_IMM16 << Split16(INT8T_MAX + globals::IMM_5);
742 expected << Opcode::JMP_IMM16 << Split16(INT8T_MAX + globals::IMM_2);
743 expected << Opcode::JMP_IMM16 << Split16(INT8T_MAX + 1);
744 for (size_t i = 0; i < n + globals::IMM_2; i++) {
745 expected << Opcode::RETURN_VOID;
746 }
747 ASSERT_EQ(expected, out);
748 }
749
TEST(BytecodeEmitter,UnboundLabel)750 TEST(BytecodeEmitter, UnboundLabel)
751 {
752 BytecodeEmitter emitter;
753 Label label = emitter.CreateLabel();
754 emitter.Bind(label);
755 std::vector<uint8_t> out;
756 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
757 }
758
TEST(BytecodeEmitter,JumpToUnboundLabel)759 TEST(BytecodeEmitter, JumpToUnboundLabel)
760 {
761 BytecodeEmitter emitter;
762 Label label = emitter.CreateLabel();
763 emitter.Jmp(label);
764 std::vector<uint8_t> out;
765 ASSERT_EQ(BytecodeEmitter::ErrorCode::UNBOUND_LABELS, emitter.Build(&out));
766 }
767
TEST(BytecodeEmitter,JumpToUnboundLabel2)768 TEST(BytecodeEmitter, JumpToUnboundLabel2)
769 {
770 BytecodeEmitter emitter;
771 Label label1 = emitter.CreateLabel();
772 Label label2 = emitter.CreateLabel();
773 emitter.Jmp(label1);
774 emitter.Bind(label2);
775 emitter.Mov(0, 1);
776 std::vector<uint8_t> out;
777 ASSERT_EQ(BytecodeEmitter::ErrorCode::UNBOUND_LABELS, emitter.Build(&out));
778 }
779
TEST(BytecodeEmitter,TwoJumpsToOneLabel)780 TEST(BytecodeEmitter, TwoJumpsToOneLabel)
781 {
782 BytecodeEmitter emitter;
783 Label label = emitter.CreateLabel();
784 emitter.Bind(label);
785 emitter.Mov(0, 1);
786 emitter.Jmp(label);
787 emitter.Jmp(label);
788 std::vector<uint8_t> out;
789 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
790 }
791
TestNoneFormat(Opcode opcode,const std::function<void (BytecodeEmitter *)> & emit)792 static void TestNoneFormat(Opcode opcode, const std::function<void(BytecodeEmitter *)> &emit)
793 {
794 BytecodeEmitter emitter;
795 emit(&emitter);
796 std::vector<uint8_t> out;
797 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
798 std::vector<uint8_t> expected;
799 expected << opcode;
800 ASSERT_EQ(expected, out);
801 }
802
803 // NOLINTEND(readability-magic-numbers,hicpp-signed-bitwise)
804
805 #include <bytecode_emitter_tests_gen.h>
806
807 } // namespace ark::panda_file::test
808