1/*
2 * Copyright (c) 2022 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 "ecmascript/compiler/assembler/x64/assembler_x64.h"
17
18#include <ostream>
19
20#include "ecmascript/compiler/assembler/aarch64/assembler_aarch64.h"
21#include "ecmascript/compiler/assembler/x64/extended_assembler_x64.h"
22#include "ecmascript/compiler/codegen/llvm/llvm_codegen.h"
23#include "ecmascript/compiler/trampoline/x64/common_call.h"
24#include "ecmascript/ecma_vm.h"
25#include "ecmascript/mem/dyn_chunk.h"
26#include "ecmascript/tests/test_helper.h"
27
28#include "llvm-c/Analysis.h"
29#include "llvm-c/Core.h"
30#include "llvm-c/Disassembler.h"
31#include "llvm-c/ExecutionEngine.h"
32#include "llvm-c/Target.h"
33
34
35namespace panda::test {
36using namespace panda::ecmascript;
37using namespace panda::ecmascript::x64;
38
39class AssemblerX64Test : public testing::Test {
40public:
41    static void SetUpTestCase()
42    {
43        GTEST_LOG_(INFO) << "SetUpTestCase";
44    }
45
46    static void TearDownTestCase()
47    {
48        GTEST_LOG_(INFO) << "TearDownCase";
49    }
50
51    void SetUp() override
52    {
53        InitializeLLVM(TARGET_X64);
54        TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
55        chunk_ = thread->GetEcmaVM()->GetChunk();
56    }
57
58    void TearDown() override
59    {
60        TestHelper::DestroyEcmaVMWithScope(instance, scope);
61    }
62
63    static const char *SymbolLookupCallback([[maybe_unused]] void *disInfo, [[maybe_unused]] uint64_t referenceValue,
64                                            uint64_t *referenceType, [[maybe_unused]] uint64_t referencePC,
65                                            [[maybe_unused]] const char **referenceName)
66    {
67        *referenceType = LLVMDisassembler_ReferenceType_InOut_None;
68        return nullptr;
69    }
70
71    void InitializeLLVM(std::string triple)
72    {
73        if (triple.compare(TARGET_X64) == 0) {
74            LLVMInitializeX86TargetInfo();
75            LLVMInitializeX86TargetMC();
76            LLVMInitializeX86Disassembler();
77            /* this method must be called, ohterwise "Target does not support MC emission" */
78            LLVMInitializeX86AsmPrinter();
79            LLVMInitializeX86AsmParser();
80            LLVMInitializeX86Target();
81        } else if (triple.compare(TARGET_AARCH64) == 0) {
82            LLVMInitializeAArch64TargetInfo();
83            LLVMInitializeAArch64TargetMC();
84            LLVMInitializeAArch64Disassembler();
85            LLVMInitializeAArch64AsmPrinter();
86            LLVMInitializeAArch64AsmParser();
87            LLVMInitializeAArch64Target();
88        } else {
89            LOG_ECMA(FATAL) << "this branch is unreachable";
90            UNREACHABLE();
91        }
92    }
93
94    void DisassembleChunk(const char *triple, Assembler *assemlber, std::ostream &os)
95    {
96        LLVMDisasmContextRef dcr = LLVMCreateDisasm(triple, nullptr, 0, nullptr, SymbolLookupCallback);
97        uint8_t *byteSp = assemlber->GetBegin();
98        size_t numBytes = assemlber->GetCurrentPosition();
99        unsigned pc = 0;
100        const char outStringSize = 100;
101        char outString[outStringSize];
102        while (numBytes > 0) {
103            size_t InstSize = LLVMDisasmInstruction(dcr, byteSp, numBytes, pc, outString, outStringSize);
104            if (InstSize == 0) {
105                // 8 : 8 means width of the pc offset and instruction code
106                os << std::setw(8) << std::setfill('0') << std::hex << pc << ":" << std::setw(8)
107                   << *reinterpret_cast<uint32_t *>(byteSp) << "maybe constant" << std::endl;
108                pc += 4; // 4 pc length
109                byteSp += 4; // 4 sp offset
110                numBytes -= 4; // 4 num bytes
111            }
112            // 8 : 8 means width of the pc offset and instruction code
113            os << std::setw(8) << std::setfill('0') << std::hex << pc << ":" << std::setw(8)
114               << *reinterpret_cast<uint32_t *>(byteSp) << " " << outString << std::endl;
115            pc += InstSize;
116            byteSp += InstSize;
117            numBytes -= InstSize;
118        }
119        LLVMDisasmDispose(dcr);
120    }
121
122    EcmaVM *instance {nullptr};
123    JSThread *thread {nullptr};
124    EcmaHandleScope *scope {nullptr};
125    Chunk *chunk_ {nullptr};
126};
127
128#define __ masm.
129HWTEST_F_L0(AssemblerX64Test, Emit)
130{
131    x64::AssemblerX64 masm(chunk_);
132    Label lable1;
133
134    size_t current = 0;
135    __ Pushq(rbp);
136    uint32_t value = masm.GetU8(current++);
137    ASSERT_EQ(value, 0x55U);
138    __ Pushq(0);
139    value = masm.GetU8(current++);
140    ASSERT_EQ(value, 0x6AU);
141    value = masm.GetU8(current++);
142    ASSERT_EQ(value, 0x00U);
143    __ Popq(rbp);
144    value = masm.GetU8(current++);
145    ASSERT_EQ(value, 0x5DU);
146    __ Bind(&lable1);
147    __ Movq(rcx, rbx);
148    value = masm.GetU8(current++);
149    ASSERT_EQ(value, 0x48U);
150    value = masm.GetU8(current++);
151    ASSERT_EQ(value, 0x89U);
152    value = masm.GetU8(current++);
153    ASSERT_EQ(value, 0xCBU);
154    __ Movq(Operand(rsp, 0x40U), rbx);
155    value = masm.GetU8(current++);
156    ASSERT_EQ(value, 0x48U);
157    value = masm.GetU8(current++);
158    ASSERT_EQ(value, 0x8BU);
159    value = masm.GetU8(current++);
160    ASSERT_EQ(value, 0x5CU);
161    value = masm.GetU8(current++);
162    ASSERT_EQ(value, 0x24U);
163    value = masm.GetU8(current++);
164    ASSERT_EQ(value, 0x40U);
165    __ Jmp(&lable1);
166    value = masm.GetU8(current++);
167    ASSERT_EQ(value, 0xEBU);
168    value = masm.GetU8(current++);
169    ASSERT_EQ(value, 0xF6U);
170    __ Ret();
171    value = masm.GetU8(current++);
172    ASSERT_EQ(value, 0xC3U);
173
174    ecmascript::kungfu::LLVMAssembler::Disassemble(nullptr, TARGET_X64,
175                                                   masm.GetBegin(), masm.GetCurrentPosition());
176}
177
178HWTEST_F_L0(AssemblerX64Test, Emit1)
179{
180    x64::AssemblerX64 masm(chunk_);
181
182    size_t current = 0;
183    __ Movl(Operand(rax, 0x38), rax);
184    uint32_t value = masm.GetU8(current++);
185    ASSERT_EQ(value, 0x8BU);
186    value = masm.GetU8(current++);
187    ASSERT_EQ(value, 0x40U);
188    value = masm.GetU8(current++);
189    ASSERT_EQ(value, 0x38U);
190
191    // 41 89 f6 movl    %esi, %r14d
192    __ Movl(rsi, r14);
193    value = masm.GetU8(current++);
194    ASSERT_EQ(value, 0x41U);
195    value = masm.GetU8(current++);
196    ASSERT_EQ(value, 0x89U);
197    value = masm.GetU8(current++);
198    ASSERT_EQ(value, 0xF6U);
199
200    // movzbq  (%rcx), %rax
201    __ Movzbq(Operand(rcx, 0), rax);
202    value = masm.GetU8(current++);
203    ASSERT_EQ(value, 0x48U);
204    value = masm.GetU8(current++);
205    ASSERT_EQ(value, 0x0FU);
206    value = masm.GetU8(current++);
207    ASSERT_EQ(value, 0xB6U);
208    value = masm.GetU8(current++);
209    ASSERT_EQ(value, 0x01U);
210
211    // 48 ba 02 00 00 00 00 00 00 00   movabs $0x2,%rdx
212    __ Movabs(0x2, rdx);
213    value = masm.GetU8(current++);
214    ASSERT_EQ(value, 0x48U);
215    value = masm.GetU8(current++);
216    ASSERT_EQ(value, 0xBAU);
217    value = masm.GetU8(current++);
218    ASSERT_EQ(value, 0x02U);
219    value = masm.GetU8(current++);
220    ASSERT_EQ(value, 0x00U);
221    value = masm.GetU8(current++);
222    ASSERT_EQ(value, 0x00U);
223    value = masm.GetU8(current++);
224    ASSERT_EQ(value, 0x00U);
225    value = masm.GetU8(current++);
226    ASSERT_EQ(value, 0x00U);
227    value = masm.GetU8(current++);
228    ASSERT_EQ(value, 0x00U);
229    value = masm.GetU8(current++);
230    ASSERT_EQ(value, 0x00U);
231    value = masm.GetU8(current++);
232    ASSERT_EQ(value, 0x00U);
233
234    __ Movq(0x5, rdx);
235    value = masm.GetU8(current++);
236    ASSERT_EQ(value, 0xBAU);
237    value = masm.GetU8(current++);
238    ASSERT_EQ(value, 0x05U);
239    value = masm.GetU8(current++);
240    ASSERT_EQ(value, 0x00U);
241    value = masm.GetU8(current++);
242    ASSERT_EQ(value, 0x00U);
243    value = masm.GetU8(current++);
244    ASSERT_EQ(value, 0x00U);
245
246    // 49 89 e0        mov    %rsp,%r8
247    __ Movq(rsp, r8);
248    value = masm.GetU8(current++);
249    ASSERT_EQ(value, 0x49U);
250    value = masm.GetU8(current++);
251    ASSERT_EQ(value, 0x89U);
252    value = masm.GetU8(current++);
253    ASSERT_EQ(value, 0xE0U);
254
255    ecmascript::kungfu::LLVMAssembler::Disassemble(nullptr, TARGET_X64,
256                                                   masm.GetBegin(), masm.GetCurrentPosition());
257}
258
259HWTEST_F_L0(AssemblerX64Test, Emit2)
260{
261    x64::AssemblerX64 masm(chunk_);
262
263    size_t current = 0;
264    // 81 fa ff ff ff 09       cmpl    $0x9ffffff,%edx
265    __ Cmpl(0x9FFFFFF, rdx);
266    uint32_t value = masm.GetU8(current++);
267    ASSERT_EQ(value, 0x81U);
268    value = masm.GetU8(current++);
269    ASSERT_EQ(value, 0xFAU);
270    value = masm.GetU8(current++);
271    ASSERT_EQ(value, 0xFFU);
272    value = masm.GetU8(current++);
273    ASSERT_EQ(value, 0xFFU);
274    value = masm.GetU8(current++);
275    ASSERT_EQ(value, 0xFFU);
276    value = masm.GetU8(current++);
277    ASSERT_EQ(value, 0x09U);
278
279    // 39 cb   cmpl    %ecx,%ebx
280    __ Cmpl(rcx, rbx);
281    value = masm.GetU8(current++);
282    ASSERT_EQ(value, 0x39U);
283    value = masm.GetU8(current++);
284    ASSERT_EQ(value, 0xCBU);
285
286    // 48 83 fa 00     cmp    $0x0,%rdx
287    __ Cmp(0x0, rdx);
288    value = masm.GetU8(current++);
289    ASSERT_EQ(value, 0x48U);
290    value = masm.GetU8(current++);
291    ASSERT_EQ(value, 0x83U);
292    value = masm.GetU8(current++);
293    ASSERT_EQ(value, 0xFAU);
294    value = masm.GetU8(current++);
295    ASSERT_EQ(value, 0x00U);
296
297    // 4c 39 D8 cmpq    %r11, %rax
298    __ Cmpq(r11, rax);
299    value = masm.GetU8(current++);
300    ASSERT_EQ(value, 0x4CU);
301    value = masm.GetU8(current++);
302    ASSERT_EQ(value, 0x39U);
303    value = masm.GetU8(current++);
304    ASSERT_EQ(value, 0xD8U);
305
306
307    // 0f ba e0 08     bt     $0x8,%eax
308    __ Btl(0x8, rax);
309    value = masm.GetU8(current++);
310    ASSERT_EQ(value, 0x0FU);
311    value = masm.GetU8(current++);
312    ASSERT_EQ(value, 0xBAU);
313    value = masm.GetU8(current++);
314    ASSERT_EQ(value, 0xE0U);
315    value = masm.GetU8(current++);
316    ASSERT_EQ(value, 0x08U);
317    ecmascript::kungfu::LLVMAssembler::Disassemble(nullptr, TARGET_X64,
318                                                   masm.GetBegin(), masm.GetCurrentPosition());
319}
320
321HWTEST_F_L0(AssemblerX64Test, Emit3)
322{
323    x64::AssemblerX64 masm(chunk_);
324    size_t current = 0;
325
326    // cmovbe  %ebx, %ecx
327    __ CMovbe(rbx, rcx);
328    uint32_t value = masm.GetU8(current++);
329
330    ASSERT_EQ(value, 0x0FU);
331    value = masm.GetU8(current++);
332    ASSERT_EQ(value, 0x46U);
333    value = masm.GetU8(current++);
334    ASSERT_EQ(value, 0xCBU);
335
336    // testb   $0x1, %r14b
337    __ Testb(0x1, r14);
338    value = masm.GetU8(current++);
339    ASSERT_EQ(value, 0x41U);
340    value = masm.GetU8(current++);
341    ASSERT_EQ(value, 0xF6U);
342    value = masm.GetU8(current++);
343    ASSERT_EQ(value, 0xC6U);
344    value = masm.GetU8(current++);
345    ASSERT_EQ(value, 0x01U);
346
347    // 48 f6 c4 0f testq   $15, %rsp
348    __ Testq(15, rsp);
349    value = masm.GetU8(current++);
350    ASSERT_EQ(value, 0x40U);
351    value = masm.GetU8(current++);
352    ASSERT_EQ(value, 0xF6U);
353    value = masm.GetU8(current++);
354    ASSERT_EQ(value, 0xC4U);
355    value = masm.GetU8(current++);
356    ASSERT_EQ(value, 0x0FU);
357
358    // andq    $ASM_JS_METHOD_NUM_VREGS_MASK, %r11
359    __ Andq(0xfffffff, r11);
360    value = masm.GetU8(current++);
361    ASSERT_EQ(value, 0x49U);
362    value = masm.GetU8(current++);
363    ASSERT_EQ(value, 0x81U);
364    value = masm.GetU8(current++);
365    ASSERT_EQ(value, 0xE3U);
366    value = masm.GetU8(current++);
367    ASSERT_EQ(value, 0xFFU);
368    value = masm.GetU8(current++);
369    ASSERT_EQ(value, 0xFFU);
370    value = masm.GetU8(current++);
371    ASSERT_EQ(value, 0xFFU);
372    value = masm.GetU8(current++);
373    ASSERT_EQ(value, 0x0FU);
374
375    // andl 0xfffffff, %eax
376    __ Andl(0xfffffff, rax);
377    value = masm.GetU8(current++);
378    ASSERT_EQ(value, 0x25U);
379    value = masm.GetU8(current++);
380    ASSERT_EQ(value, 0xFFU);
381    value = masm.GetU8(current++);
382    ASSERT_EQ(value, 0xFFU);
383    value = masm.GetU8(current++);
384    ASSERT_EQ(value, 0xFFU);
385    value = masm.GetU8(current++);
386    ASSERT_EQ(value, 0x0FU);
387
388    // and     %rax, %rdx
389    __ And(rax, rdx);
390    value = masm.GetU8(current++);
391    ASSERT_EQ(value, 0x48U);
392    value = masm.GetU8(current++);
393    ASSERT_EQ(value, 0x21U);
394    value = masm.GetU8(current++);
395    ASSERT_EQ(value, 0xC2U);
396    ecmascript::kungfu::LLVMAssembler::Disassemble(nullptr, TARGET_X64,
397                                                   masm.GetBegin(), masm.GetCurrentPosition());
398}
399
400HWTEST_F_L0(AssemblerX64Test, Emit4)
401{
402    x64::AssemblerX64 masm(chunk_);
403    size_t current = 0;
404
405    // 4a 8d 0c f5 00 00 00 00 leaq    0x0(,%r14,8),%rcx
406    __ Leaq(Operand(r14, Scale::Times8, 0), rcx);
407    uint32_t value = masm.GetU8(current++);
408    ASSERT_EQ(value, 0x4AU);
409    value = masm.GetU8(current++);
410    ASSERT_EQ(value, 0x8DU);
411    value = masm.GetU8(current++);
412    ASSERT_EQ(value, 0x0CU);
413    value = masm.GetU8(current++);
414    ASSERT_EQ(value, 0xF5U);
415    value = masm.GetU8(current++);
416    ASSERT_EQ(value, 0x00U);
417    value = masm.GetU8(current++);
418    ASSERT_EQ(value, 0x00U);
419    value = masm.GetU8(current++);
420    ASSERT_EQ(value, 0x00U);
421    value = masm.GetU8(current++);
422    ASSERT_EQ(value, 0x00U);
423
424    // 8d 90 ff ff ff fc       leal    -0x3000001(%rax),%edx
425    __ Leal(Operand(rax, -50331649), rdx);
426    value = masm.GetU8(current++);
427    ASSERT_EQ(value, 0x8DU);
428    value = masm.GetU8(current++);
429    ASSERT_EQ(value, 0x90U);
430    value = masm.GetU8(current++);
431    ASSERT_EQ(value, 0xFFU);
432    value = masm.GetU8(current++);
433    ASSERT_EQ(value, 0xFFU);
434    value = masm.GetU8(current++);
435    ASSERT_EQ(value, 0xFFU);
436    value = masm.GetU8(current++);
437    ASSERT_EQ(value, 0xFCU);
438
439    // c1 e0 18        shl    $0x18,%eax
440    __ Shll(0x18, rax);
441    value = masm.GetU8(current++);
442    ASSERT_EQ(value, 0xC1U);
443    value = masm.GetU8(current++);
444    ASSERT_EQ(value, 0xE0U);
445    value = masm.GetU8(current++);
446    ASSERT_EQ(value, 0x18U);
447
448    // shrq    $ASM_JS_METHOD_NUM_ARGS_START_BIT(32), %r11
449    __ Shrq(32, r11);
450    value = masm.GetU8(current++);
451    ASSERT_EQ(value, 0x49U);
452    value = masm.GetU8(current++);
453    ASSERT_EQ(value, 0xC1U);
454    value = masm.GetU8(current++);
455    ASSERT_EQ(value, 0xEBU);
456    value = masm.GetU8(current++);
457    ASSERT_EQ(value, 0x20U);
458
459    // int3
460    __ Int3();
461    value = masm.GetU8(current++);
462    ASSERT_EQ(value, 0xCCU);
463    ecmascript::kungfu::LLVMAssembler::Disassemble(nullptr, TARGET_X64,
464                                                   masm.GetBegin(), masm.GetCurrentPosition());
465}
466#undef __
467}  // namespace panda::test
468