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 */
EmitJmpFwdBwd(size_t n1,size_t n2)273 static std::vector<uint8_t> EmitJmpFwdBwd(size_t n1, size_t n2)
274 {
275 std::array<std::tuple<size_t, int32_t, int32_t>, 3U> jmps {
276 std::tuple {globals::IMM_2, std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()},
277 std::tuple {globals::IMM_3, std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max()},
278 std::tuple {globals::IMM_5, std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max()}};
279
280 std::vector<uint8_t> out;
281
282 size_t jmpSize1;
283 size_t jmpSize2;
284 int32_t immMax1;
285 int32_t immMin2;
286
287 for (const auto &t1 : jmps) {
288 std::tie(jmpSize1, std::ignore, immMax1) = t1;
289
290 for (const auto &t2 : jmps) {
291 std::tie(jmpSize2, immMin2, std::ignore) = t2;
292
293 int32_t imm1 = jmpSize1 + n1 + jmpSize2 + n2;
294 int32_t imm2 = jmpSize1 + n1;
295
296 if (imm1 <= immMax1 && -imm2 >= immMin2) {
297 EmitJmp(GetOpcode(jmpSize1), imm1, &out);
298
299 for (size_t i = 0; i < n1; i++) {
300 out << Opcode::RETURN_VOID;
301 }
302
303 EmitJmp(GetOpcode(jmpSize2), -imm2, &out);
304
305 for (size_t i = 0; i < n2; i++) {
306 out << Opcode::RETURN_VOID;
307 }
308
309 out << Opcode::RETURN_VOID;
310
311 return out;
312 }
313 }
314 }
315
316 return out;
317 }
318
319 /*
320 * Test following control flow:
321 *
322 * label1:
323 * jmp label2
324 * ... <- n1 return.void instructions
325 * jmp label1
326 * ... <- n2 return.void instructions
327 * label2:
328 * return.void
329 */
TestJmpFwdBwd(size_t n1,size_t n2)330 void TestJmpFwdBwd(size_t n1, size_t n2)
331 {
332 BytecodeEmitter emitter;
333 Label label1 = emitter.CreateLabel();
334 Label label2 = emitter.CreateLabel();
335
336 emitter.Bind(label1);
337 emitter.Jmp(label2);
338 for (size_t i = 0; i < n1; ++i) {
339 emitter.ReturnVoid();
340 }
341 emitter.Jmp(label1);
342 for (size_t i = 0; i < n2; ++i) {
343 emitter.ReturnVoid();
344 }
345 emitter.Bind(label2);
346 emitter.ReturnVoid();
347
348 std::vector<uint8_t> out;
349 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)) << "n1 = " << n1 << " n2 = " << n2;
350
351 ASSERT_EQ(EmitJmpFwdBwd(n1, n2), out) << "n1 = " << n1 << " n2 = " << n2;
352 }
353
TEST(BytecodeEmitter,JmpFwdBwd)354 TEST(BytecodeEmitter, JmpFwdBwd)
355 {
356 // fwd jmp imm16
357 // bwd jmp imm8
358 TestJmpFwdBwd(0, std::numeric_limits<int8_t>::max());
359
360 // fwd jmp imm16
361 // bwd jmp imm16
362 TestJmpFwdBwd(std::numeric_limits<int8_t>::max(), 0);
363
364 // fwd jmp imm32
365 // bwd jmp imm8
366 TestJmpFwdBwd(0, std::numeric_limits<int16_t>::max());
367
368 // fwd jmp imm32
369 // bwd jmp imm16
370 TestJmpFwdBwd(std::numeric_limits<int8_t>::max(), std::numeric_limits<int16_t>::max());
371
372 // fwd jmp imm32
373 // bwd jmp imm32
374 TestJmpFwdBwd(std::numeric_limits<int16_t>::max(), 0);
375 }
376
TEST(BytecodeEmitter,JmpBwd_IMM32)377 TEST(BytecodeEmitter, JmpBwd_IMM32)
378 {
379 BytecodeEmitter emitter;
380 Label label = emitter.CreateLabel();
381 emitter.Bind(label);
382 int numRet = -std::numeric_limits<int16_t>::min() + 1;
383 for (int i = 0; i < numRet; ++i) {
384 emitter.ReturnVoid();
385 }
386 emitter.Jmp(label);
387 std::vector<uint8_t> out;
388 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
389 std::vector<uint8_t> expected;
390 for (int i = 0; i < numRet; ++i) {
391 expected << Opcode::RETURN_VOID;
392 }
393 expected << Opcode::JMP_IMM32 << Split32(-numRet);
394 ASSERT_EQ(expected, out);
395 }
396
TEST(BytecodeEmitter,JmpFwd_IMM32)397 TEST(BytecodeEmitter, JmpFwd_IMM32)
398 {
399 BytecodeEmitter emitter;
400 Label label = emitter.CreateLabel();
401 emitter.Jmp(label);
402 // -4 because 2 bytes takes jmp itself and
403 // emitter estimate length of jmp by 3 greater the it is actually
404 // and plus one byte to make 16bit overflow
405 int numRet = std::numeric_limits<int16_t>::max() - globals::IMM_4;
406 for (int i = 0; i < numRet; ++i) {
407 emitter.ReturnVoid();
408 }
409 emitter.Bind(label);
410 emitter.ReturnVoid();
411 std::vector<uint8_t> out;
412 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
413 std::vector<uint8_t> expected;
414 expected << Opcode::JMP_IMM32 << Split32(numRet + globals::IMM_5);
415 for (int i = 0; i < numRet + 1; ++i) {
416 expected << Opcode::RETURN_VOID;
417 }
418 ASSERT_EQ(expected, out);
419 }
420
421 // NOLINTNEXTLINE(readability-identifier-naming)
JcmpBwd_V8_IMM8(Opcode opcode,const std::function<void (BytecodeEmitter *,uint8_t,const Label & label)> & emitJcmp)422 static void JcmpBwd_V8_IMM8(Opcode opcode,
423 const std::function<void(BytecodeEmitter *, uint8_t, const Label &label)> &emitJcmp)
424 {
425 BytecodeEmitter emitter;
426 Label label = emitter.CreateLabel();
427 emitter.Bind(label);
428 int numRet = globals::IMM_15;
429 for (int i = 0; i < numRet; ++i) {
430 emitter.ReturnVoid();
431 }
432 emitJcmp(&emitter, globals::IMM_15, label);
433 std::vector<uint8_t> out;
434 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
435 std::vector<uint8_t> expected;
436 for (int i = 0; i < numRet; ++i) {
437 expected << Opcode::RETURN_VOID;
438 }
439
440 expected << opcode << 15U << static_cast<uint8_t>(-numRet);
441 ASSERT_EQ(expected, out);
442 }
443
444 // NOLINTNEXTLINE(readability-identifier-naming)
JcmpFwd_V8_IMM8(Opcode opcode,const std::function<void (BytecodeEmitter *,uint8_t,const Label & label)> & emitJcmp)445 static void JcmpFwd_V8_IMM8(Opcode opcode,
446 const std::function<void(BytecodeEmitter *, uint8_t, const Label &label)> &emitJcmp)
447 {
448 BytecodeEmitter emitter;
449 Label label = emitter.CreateLabel();
450 emitJcmp(&emitter, globals::IMM_15, label);
451 int numRet = globals::IMM_12;
452 for (int i = 0; i < numRet; ++i) {
453 emitter.ReturnVoid();
454 }
455 emitter.Bind(label);
456 emitter.ReturnVoid();
457 std::vector<uint8_t> out;
458 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
459 std::vector<uint8_t> expected;
460 // 2 bytes takes jmp itself and plus one byte to make.
461 expected << opcode << 15U << static_cast<uint8_t>(numRet + globals::IMM_3);
462
463 for (int i = 0; i < numRet + 1; ++i) {
464 expected << Opcode::RETURN_VOID;
465 }
466
467 ASSERT_EQ(expected, out);
468 }
469
470 // NOLINTNEXTLINE(readability-identifier-naming)
JcmpBwd_V8_IMM16(Opcode opcode,const std::function<void (BytecodeEmitter *,uint8_t,const Label & label)> & emitJcmp)471 static void JcmpBwd_V8_IMM16(Opcode opcode,
472 const std::function<void(BytecodeEmitter *, uint8_t, const Label &label)> &emitJcmp)
473 {
474 {
475 // Test min imm value
476 BytecodeEmitter emitter;
477 Label label = emitter.CreateLabel();
478 emitter.Bind(label);
479 int numRet = -std::numeric_limits<int8_t>::min();
480 ++numRet;
481 for (int i = 0; i < numRet; ++i) {
482 emitter.ReturnVoid();
483 }
484 emitJcmp(&emitter, 0, label);
485 std::vector<uint8_t> out;
486 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
487 std::vector<uint8_t> expected;
488 for (int i = 0; i < numRet; ++i) {
489 expected << Opcode::RETURN_VOID;
490 }
491 expected << opcode << 0U << Split16(-numRet);
492 ASSERT_EQ(expected, out);
493 }
494 {
495 // Test max imm value
496 BytecodeEmitter emitter;
497 Label label = emitter.CreateLabel();
498 emitter.Bind(label);
499 int numRet = -std::numeric_limits<int16_t>::min();
500 for (int i = 0; i < numRet; ++i) {
501 emitter.ReturnVoid();
502 }
503 emitJcmp(&emitter, 0, label);
504 std::vector<uint8_t> out;
505 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
506 std::vector<uint8_t> expected;
507 for (int i = 0; i < numRet; ++i) {
508 expected << Opcode::RETURN_VOID;
509 }
510 expected << opcode << 0U << Split16(-numRet);
511 ASSERT_EQ(expected, out);
512 }
513 }
514
515 // NOLINTNEXTLINE(readability-identifier-naming)
JcmpFwd_V8_IMM16(Opcode opcode,const std::function<void (BytecodeEmitter *,uint8_t,const Label & label)> & emitJcmp)516 static void JcmpFwd_V8_IMM16(Opcode opcode,
517 const std::function<void(BytecodeEmitter *, uint8_t, const Label &label)> &emitJcmp)
518 {
519 {
520 // Test min imm
521 BytecodeEmitter emitter;
522 Label label = emitter.CreateLabel();
523 emitJcmp(&emitter, 0, label);
524 // -3 because 4 bytes takes jmp itself
525 // plus one to make 8bit overflow
526 int numRet = std::numeric_limits<int8_t>::max() - globals::IMM_3;
527 for (int i = 0; i < numRet; ++i) {
528 emitter.ReturnVoid();
529 }
530 emitter.Bind(label);
531 emitter.ReturnVoid();
532 std::vector<uint8_t> out;
533 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
534 std::vector<uint8_t> expected;
535 expected << opcode << 0U << Split16(numRet + globals::IMM_4);
536 for (int i = 0; i < numRet + 1; ++i) {
537 expected << Opcode::RETURN_VOID;
538 }
539 ASSERT_EQ(expected, out);
540 }
541 {
542 // Test max imm
543 BytecodeEmitter emitter;
544 Label label = emitter.CreateLabel();
545 emitJcmp(&emitter, 0, label);
546 // -4 because 4 bytes takes jmp itself
547 int numRet = std::numeric_limits<int16_t>::max() - globals::IMM_4;
548 for (int i = 0; i < numRet; ++i) {
549 emitter.ReturnVoid();
550 }
551 emitter.Bind(label);
552 emitter.ReturnVoid();
553 std::vector<uint8_t> out;
554 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
555 std::vector<uint8_t> expected;
556 expected << opcode << 0U << Split16(numRet + globals::IMM_4);
557 for (int i = 0; i < numRet + 1; ++i) {
558 expected << Opcode::RETURN_VOID;
559 }
560 ASSERT_EQ(expected, out);
561 }
562 }
563
TEST(BytecodeEmitter,Jne_V8_IMM8)564 TEST(BytecodeEmitter, Jne_V8_IMM8)
565 {
566 BytecodeEmitter emitter;
567 Label label = emitter.CreateLabel();
568 emitter.Jne(0, label);
569 emitter.Bind(label);
570 emitter.ReturnVoid();
571 std::vector<uint8_t> out;
572 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
573 std::vector<uint8_t> expected;
574 expected << Opcode::JNE_V8_IMM8 << 0 << globals::IMM_3 << Opcode::RETURN_VOID;
575 ASSERT_EQ(expected, out);
576 }
577
TEST(BytecodeEmitter,Jne_V8_IMM16)578 TEST(BytecodeEmitter, Jne_V8_IMM16)
579 {
580 BytecodeEmitter emitter;
581 Label label = emitter.CreateLabel();
582 static constexpr uint8_t JNE_REG = 16;
583 static constexpr size_t N_NOPS = 256;
584 static constexpr size_t INSN_SZ = 4;
585 emitter.Jne(JNE_REG, label);
586 for (size_t i = 0; i < N_NOPS; ++i) {
587 emitter.Nop();
588 }
589 emitter.Bind(label);
590 emitter.ReturnVoid();
591 std::vector<uint8_t> out;
592 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
593 std::vector<uint8_t> expected;
594 expected << Opcode::JNE_V8_IMM16 << JNE_REG << Split16(INSN_SZ + N_NOPS);
595 for (size_t i = 0; i < N_NOPS; ++i) {
596 expected << Opcode::NOP;
597 }
598 expected << Opcode::RETURN_VOID;
599 ASSERT_EQ(expected, out);
600 }
601
602 // NOLINTNEXTLINE(readability-identifier-naming)
Jcmpz_IMM8(Opcode opcode,const std::function<void (BytecodeEmitter *,const Label & label)> & emit_jcmp)603 static void Jcmpz_IMM8(Opcode opcode, const std::function<void(BytecodeEmitter *, const Label &label)> &emit_jcmp)
604 {
605 BytecodeEmitter emitter;
606 Label label = emitter.CreateLabel();
607 emit_jcmp(&emitter, label);
608 emitter.Bind(label);
609 emitter.ReturnVoid();
610 std::vector<uint8_t> out;
611 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
612 std::vector<uint8_t> expected;
613 expected << opcode << globals::IMM_2 << Opcode::RETURN_VOID;
614 ASSERT_EQ(expected, out);
615 }
616
617 // NOLINTNEXTLINE(readability-identifier-naming)
Jcmpz_IMM16(Opcode opcode,const std::function<void (BytecodeEmitter *,const Label & label)> & emit_jcmp)618 static void Jcmpz_IMM16(Opcode opcode, const std::function<void(BytecodeEmitter *, const Label &label)> &emit_jcmp)
619 {
620 BytecodeEmitter emitter;
621 Label label = emitter.CreateLabel();
622 emit_jcmp(&emitter, label);
623 for (int i = 0; i < std::numeric_limits<uint8_t>::max() - globals::IMM_2; ++i) {
624 emitter.ReturnVoid();
625 }
626 emitter.Bind(label);
627 emitter.ReturnVoid();
628 std::vector<uint8_t> out;
629 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
630 std::vector<uint8_t> expected;
631 expected << opcode << Split16(std::numeric_limits<uint8_t>::max() + 1);
632 for (int i = 0; i < std::numeric_limits<uint8_t>::max() - 1; ++i) {
633 expected << Opcode::RETURN_VOID;
634 }
635 ASSERT_EQ(expected, out);
636 }
637
TEST(BytecodeEmitter,JmpFwdCrossRef)638 TEST(BytecodeEmitter, JmpFwdCrossRef)
639 {
640 // Situation:
641 // +---------+
642 // +----|----+ |
643 // | | | |
644 // | | v v
645 // ---*----*----*----*----
646 // lbl1 lbl2
647 BytecodeEmitter emitter;
648 Label lbl1 = emitter.CreateLabel();
649 Label lbl2 = emitter.CreateLabel();
650 emitter.Jeq(0, lbl1);
651 emitter.Jeq(0, lbl2);
652 emitter.ReturnVoid();
653 emitter.Bind(lbl1);
654 emitter.ReturnVoid();
655 for (int i = 0; i < globals::IMM_6; ++i) {
656 emitter.ReturnVoid();
657 }
658 emitter.Bind(lbl2);
659 emitter.ReturnVoid();
660 std::vector<uint8_t> out;
661 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
662
663 std::vector<uint8_t> expected;
664 expected << Opcode::JEQ_V8_IMM8 << 0U << globals::IMM_7 << Opcode::JEQ_V8_IMM8 << 0U << globals::IMM_11;
665 for (int i = 0; i < globals::IMM_9; ++i) {
666 expected << Opcode::RETURN_VOID;
667 }
668 ASSERT_EQ(expected, out);
669 }
670
TEST(BytecodeEmitter,JmpBwdCrossRef)671 TEST(BytecodeEmitter, JmpBwdCrossRef)
672 {
673 // Situation:
674 // +---------+
675 // | |
676 // +---------+ |
677 // | | | |
678 // v | | v
679 // ---*----*----*----*----
680 // lbl1 lbl2
681 BytecodeEmitter emitter;
682 Label lbl1 = emitter.CreateLabel();
683 Label lbl2 = emitter.CreateLabel();
684 emitter.Bind(lbl1);
685 emitter.ReturnVoid();
686 emitter.Jeq(0, lbl2);
687 for (int i = 0; i < globals::IMM_5; ++i) {
688 emitter.ReturnVoid();
689 }
690 emitter.Jeq(0, lbl1);
691 emitter.Bind(lbl2);
692 emitter.ReturnVoid();
693
694 std::vector<uint8_t> out;
695 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
696
697 std::vector<uint8_t> expected;
698 expected << Opcode::RETURN_VOID << Opcode::JEQ_V8_IMM8 << 0U << globals::IMM_11;
699 for (int i = 0; i < globals::IMM_5; ++i) {
700 expected << Opcode::RETURN_VOID;
701 }
702 expected << Opcode::JEQ_V8_IMM8 << 0U << static_cast<uint8_t>(-globals::IMM_9) << Opcode::RETURN_VOID;
703 ASSERT_EQ(expected, out);
704 }
705
TEST(BytecodeEmitter,Jmp3FwdCrossRefs)706 TEST(BytecodeEmitter, Jmp3FwdCrossRefs)
707 {
708 // Situation:
709 // +--------+
710 // +|--------+
711 // ||+-------+---+
712 // ||| v v
713 // ---***-------*---*
714 // lbl1 lbl2
715 BytecodeEmitter emitter;
716 Label lbl1 = emitter.CreateLabel();
717 Label lbl2 = emitter.CreateLabel();
718
719 emitter.Jmp(lbl1);
720 emitter.Jmp(lbl1);
721 emitter.Jmp(lbl2);
722
723 constexpr int32_t INT8T_MAX = std::numeric_limits<int8_t>::max();
724
725 size_t n = INT8T_MAX - globals::IMM_4;
726 for (size_t i = 0; i < n; i++) {
727 emitter.ReturnVoid();
728 }
729
730 emitter.Bind(lbl1);
731 emitter.ReturnVoid();
732 emitter.ReturnVoid();
733 emitter.Bind(lbl2);
734
735 std::vector<uint8_t> out;
736 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
737
738 std::vector<uint8_t> expected;
739 expected << Opcode::JMP_IMM16 << Split16(INT8T_MAX + globals::IMM_5);
740 expected << Opcode::JMP_IMM16 << Split16(INT8T_MAX + globals::IMM_2);
741 expected << Opcode::JMP_IMM16 << Split16(INT8T_MAX + 1);
742 for (size_t i = 0; i < n + globals::IMM_2; i++) {
743 expected << Opcode::RETURN_VOID;
744 }
745 ASSERT_EQ(expected, out);
746 }
747
TEST(BytecodeEmitter,UnboundLabel)748 TEST(BytecodeEmitter, UnboundLabel)
749 {
750 BytecodeEmitter emitter;
751 Label label = emitter.CreateLabel();
752 emitter.Bind(label);
753 std::vector<uint8_t> out;
754 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
755 }
756
TEST(BytecodeEmitter,JumpToUnboundLabel)757 TEST(BytecodeEmitter, JumpToUnboundLabel)
758 {
759 BytecodeEmitter emitter;
760 Label label = emitter.CreateLabel();
761 emitter.Jmp(label);
762 std::vector<uint8_t> out;
763 ASSERT_EQ(BytecodeEmitter::ErrorCode::UNBOUND_LABELS, emitter.Build(&out));
764 }
765
TEST(BytecodeEmitter,JumpToUnboundLabel2)766 TEST(BytecodeEmitter, JumpToUnboundLabel2)
767 {
768 BytecodeEmitter emitter;
769 Label label1 = emitter.CreateLabel();
770 Label label2 = emitter.CreateLabel();
771 emitter.Jmp(label1);
772 emitter.Bind(label2);
773 emitter.Mov(0, 1);
774 std::vector<uint8_t> out;
775 ASSERT_EQ(BytecodeEmitter::ErrorCode::UNBOUND_LABELS, emitter.Build(&out));
776 }
777
TEST(BytecodeEmitter,TwoJumpsToOneLabel)778 TEST(BytecodeEmitter, TwoJumpsToOneLabel)
779 {
780 BytecodeEmitter emitter;
781 Label label = emitter.CreateLabel();
782 emitter.Bind(label);
783 emitter.Mov(0, 1);
784 emitter.Jmp(label);
785 emitter.Jmp(label);
786 std::vector<uint8_t> out;
787 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
788 }
789
TestNoneFormat(Opcode opcode,const std::function<void (BytecodeEmitter *)> & emit)790 static void TestNoneFormat(Opcode opcode, const std::function<void(BytecodeEmitter *)> &emit)
791 {
792 BytecodeEmitter emitter;
793 emit(&emitter);
794 std::vector<uint8_t> out;
795 ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out));
796 std::vector<uint8_t> expected;
797 expected << opcode;
798 ASSERT_EQ(expected, out);
799 }
800
801 // NOLINTEND(readability-magic-numbers,hicpp-signed-bitwise)
802
803 #include <bytecode_emitter_tests_gen.h>
804
805 } // namespace ark::panda_file::test
806