• 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  */
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