• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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