1/*
2 * Copyright (c) 2021 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/aarch64/assembler_aarch64.h"
17
18#include <ostream>
19#include <sstream>
20
21#include "ecmascript/ecma_vm.h"
22#include "ecmascript/mem/dyn_chunk.h"
23#include "ecmascript/tests/test_helper.h"
24
25#include "llvm-c/Analysis.h"
26#include "llvm-c/Core.h"
27#include "llvm-c/Disassembler.h"
28#include "llvm-c/ExecutionEngine.h"
29#include "llvm-c/Target.h"
30
31namespace panda::test {
32using namespace panda::ecmascript;
33using namespace panda::ecmascript::aarch64;
34class AssemblerAarch64Test : public testing::Test {
35public:
36    static void SetUpTestCase()
37    {
38        GTEST_LOG_(INFO) << "SetUpTestCase";
39    }
40
41    static void TearDownTestCase()
42    {
43        GTEST_LOG_(INFO) << "TearDownCase";
44    }
45
46    void SetUp() override
47    {
48        InitializeLLVM(TARGET_AARCH64);
49        TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
50        chunk_ = thread->GetEcmaVM()->GetChunk();
51    }
52
53    void TearDown() override
54    {
55        TestHelper::DestroyEcmaVMWithScope(instance, scope);
56    }
57
58    static const char *SymbolLookupCallback([[maybe_unused]] void *disInfo, [[maybe_unused]] uint64_t referenceValue,
59                                            uint64_t *referenceType, [[maybe_unused]] uint64_t referencePC,
60                                            [[maybe_unused]] const char **referenceName)
61    {
62        *referenceType = LLVMDisassembler_ReferenceType_InOut_None;
63        return nullptr;
64    }
65
66    void InitializeLLVM(std::string triple)
67    {
68        if (triple.compare(TARGET_X64) == 0) {
69            LLVMInitializeX86TargetInfo();
70            LLVMInitializeX86TargetMC();
71            LLVMInitializeX86Disassembler();
72            /* this method must be called, ohterwise "Target does not support MC emission" */
73            LLVMInitializeX86AsmPrinter();
74            LLVMInitializeX86AsmParser();
75            LLVMInitializeX86Target();
76        } else if (triple.compare(TARGET_AARCH64) == 0) {
77            LLVMInitializeAArch64TargetInfo();
78            LLVMInitializeAArch64TargetMC();
79            LLVMInitializeAArch64Disassembler();
80            LLVMInitializeAArch64AsmPrinter();
81            LLVMInitializeAArch64AsmParser();
82            LLVMInitializeAArch64Target();
83        } else {
84            LOG_ECMA(FATAL) << "this branch is unreachable";
85            UNREACHABLE();
86        }
87    }
88
89    void DisassembleChunk(const char *triple, Assembler *assemlber, std::ostream &os)
90    {
91        LLVMDisasmContextRef dcr = LLVMCreateDisasm(triple, nullptr, 0, nullptr, SymbolLookupCallback);
92        uint8_t *byteSp = assemlber->GetBegin();
93        size_t numBytes = assemlber->GetCurrentPosition();
94        unsigned pc = 0;
95        const char outStringSize = 100;
96        char outString[outStringSize];
97        while (numBytes > 0) {
98            size_t InstSize = LLVMDisasmInstruction(dcr, byteSp, numBytes, pc, outString, outStringSize);
99            if (InstSize == 0) {
100                // 8 : 8 means width of the pc offset and instruction code
101                os << std::setw(8) << std::setfill('0') << std::hex << pc << ":" << std::setw(8)
102                   << *reinterpret_cast<uint32_t *>(byteSp) << "maybe constant" << std::endl;
103                pc += 4; // 4 pc length
104                byteSp += 4; // 4 sp offset
105                numBytes -= 4; // 4 num bytes
106            }
107            // 8 : 8 means width of the pc offset and instruction code
108            os << std::setw(8) << std::setfill('0') << std::hex << pc << ":" << std::setw(8)
109               << *reinterpret_cast<uint32_t *>(byteSp) << " " << outString << std::endl;
110            pc += InstSize;
111            byteSp += InstSize;
112            numBytes -= InstSize;
113        }
114        LLVMDisasmDispose(dcr);
115    }
116    EcmaVM *instance {nullptr};
117    JSThread *thread {nullptr};
118    EcmaHandleScope *scope {nullptr};
119    Chunk *chunk_ {nullptr};
120};
121
122#define __ masm.
123HWTEST_F_L0(AssemblerAarch64Test, Mov)
124{
125    std::string expectResult("00000000:d28acf01 \tmov\tx1, #22136\n"
126                             "00000004:f2a24681 \tmovk\tx1, #4660, lsl #16\n"
127                             "00000008:f2ffffe1 \tmovk\tx1, #65535, lsl #48\n"
128                             "0000000c:d2801de2 \tmov\tx2, #239\n"
129                             "00000010:f2b579a2 \tmovk\tx2, #43981, lsl #16\n"
130                             "00000014:f2cacf02 \tmovk\tx2, #22136, lsl #32\n"
131                             "00000018:f2e24682 \tmovk\tx2, #4660, lsl #48\n"
132                             "0000001c:b2683be3 \tmov\tx3, #549739036672\n"
133                             "00000020:f2824683 \tmovk\tx3, #4660\n"
134                             "00000024:32083fe4 \tmov\tw4, #-16776961\n");
135    AssemblerAarch64 masm(chunk_);
136    __ Mov(Register(X1),  Immediate(0xffff000012345678));
137    __ Mov(Register(X2),  Immediate(0x12345678abcd00ef));
138    __ Mov(Register(X3),  Immediate(0x7fff001234));
139    __ Mov(Register(X4).W(),  Immediate(0xff0000ff));
140    std::ostringstream oss;
141    DisassembleChunk(TARGET_AARCH64, &masm, oss);
142    ASSERT_EQ(oss.str(), expectResult);
143}
144
145HWTEST_F_L0(AssemblerAarch64Test, MovReg)
146{
147    std::string expectResult("00000000:aa0203e1 \tmov\tx1, x2\n"
148                             "00000004:910003e2 \tmov\tx2, sp\n"
149                             "00000008:2a0203e1 \tmov\tw1, w2\n");
150    AssemblerAarch64 masm(chunk_);
151    __ Mov(Register(X1),  Register(X2));
152    __ Mov(Register(X2),  Register(SP));
153    __ Mov(Register(X1, W),  Register(X2, W));
154    std::ostringstream oss;
155    DisassembleChunk(TARGET_AARCH64, &masm, oss);
156    ASSERT_EQ(oss.str(), expectResult);
157}
158
159HWTEST_F_L0(AssemblerAarch64Test, LdpStp)
160{
161    std::string expectResult("00000000:a8808be1 \tstp\tx1, x2, [sp], #8\n"
162                             "00000004:a9c08be1 \tldp\tx1, x2, [sp, #8]!\n"
163                             "00000008:a94093e3 \tldp\tx3, x4, [sp, #8]\n"
164                             "0000000c:294113e3 \tldp\tw3, w4, [sp, #8]\n");
165
166    AssemblerAarch64 masm(chunk_);
167    __ Stp(Register(X1),  Register(X2), MemoryOperand(Register(SP), 8, POSTINDEX));
168    __ Ldp(Register(X1),  Register(X2), MemoryOperand(Register(SP), 8, PREINDEX));
169    __ Ldp(Register(X3),  Register(X4), MemoryOperand(Register(SP), 8, OFFSET));
170    __ Ldp(Register(X3).W(),  Register(X4).W(), MemoryOperand(Register(SP), 8, OFFSET));
171    std::ostringstream oss;
172    DisassembleChunk(TARGET_AARCH64, &masm, oss);
173    ASSERT_EQ(oss.str(), expectResult);
174}
175
176HWTEST_F_L0(AssemblerAarch64Test, LdrStr)
177{
178    std::string expectResult("00000000:f80087e1 \tstr\tx1, [sp], #8\n"
179                             "00000004:f81f87e1 \tstr\tx1, [sp], #-8\n"
180                             "00000008:f8408fe1 \tldr\tx1, [sp, #8]!\n"
181                             "0000000c:f94007e3 \tldr\tx3, [sp, #8]\n"
182                             "00000010:b9400be3 \tldr\tw3, [sp, #8]\n"
183                             "00000014:38408fe1 \tldrb\tw1, [sp, #8]!\n"
184                             "00000018:394023e1 \tldrb\tw1, [sp, #8]\n"
185                             "0000001c:78408fe1 \tldrh\tw1, [sp, #8]!\n"
186                             "00000020:794013e1 \tldrh\tw1, [sp, #8]\n"
187                             "00000024:f85f83e1 \tldur\tx1, [sp, #-8]\n"
188                             "00000028:f81f83e3 \tstur\tx3, [sp, #-8]\n");
189
190    AssemblerAarch64 masm(chunk_);
191    __ Str(Register(X1), MemoryOperand(Register(SP), 8, POSTINDEX));
192    __ Str(Register(X1), MemoryOperand(Register(SP), -8, POSTINDEX));
193    __ Ldr(Register(X1), MemoryOperand(Register(SP), 8, PREINDEX));
194    __ Ldr(Register(X3), MemoryOperand(Register(SP), 8, OFFSET));
195    __ Ldr(Register(X3).W(), MemoryOperand(Register(SP), 8, OFFSET));
196    __ Ldrb(Register(X1).W(), MemoryOperand(Register(SP), 8, PREINDEX));
197    __ Ldrb(Register(X1).W(), MemoryOperand(Register(SP), 8, OFFSET));
198    __ Ldrh(Register(X1).W(), MemoryOperand(Register(SP), 8, PREINDEX));
199    __ Ldrh(Register(X1).W(), MemoryOperand(Register(SP), 8, OFFSET));
200    __ Ldur(Register(X1), MemoryOperand(Register(SP), -8, OFFSET));
201    __ Stur(Register(X3), MemoryOperand(Register(SP), -8, OFFSET));
202    std::ostringstream oss;
203    DisassembleChunk(TARGET_AARCH64, &masm, oss);
204    ASSERT_EQ(oss.str(), expectResult);
205}
206
207HWTEST_F_L0(AssemblerAarch64Test, AddSub)
208{
209    std::string expectResult("00000000:910023ff \tadd\tsp, sp, #8\n"
210                             "00000004:d10023ff \tsub\tsp, sp, #8\n"
211                             "00000008:8b020021 \tadd\tx1, x1, x2\n"
212                             "0000000c:8b030c41 \tadd\tx1, x2, x3, lsl #3\n"
213                             "00000010:8b234c41 \tadd\tx1, x2, w3, uxtw #3\n"
214                             "00000014:8b224fff \tadd\tsp, sp, w2, uxtw #3\n");
215    AssemblerAarch64 masm(chunk_);
216    __ Add(Register(SP), Register(SP), Immediate(8));
217    __ Add(Register(SP), Register(SP), Immediate(-8));
218    __ Add(Register(X1), Register(X1), Operand(Register(X2)));
219    __ Add(Register(X1), Register(X2), Operand(Register(X3), LSL, 3));
220    __ Add(Register(X1), Register(X2), Operand(Register(X3), UXTW, 3));
221    __ Add(Register(SP), Register(SP), Operand(Register(X2), UXTW, 3));
222
223    std::ostringstream oss;
224    DisassembleChunk(TARGET_AARCH64, &masm, oss);
225    ASSERT_EQ(oss.str(), expectResult);
226}
227
228HWTEST_F_L0(AssemblerAarch64Test, CMP)
229{
230    std::string expectResult("00000000:eb02003f \tcmp\tx1, x2\n"
231                             "00000004:f100203f \tcmp\tx1, #8\n");
232    AssemblerAarch64 masm(chunk_);
233    __ Cmp(Register(X1), Register(X2));
234    __ Cmp(Register(X1), Immediate(8));
235
236    std::ostringstream oss;
237    DisassembleChunk(TARGET_AARCH64, &masm, oss);
238    ASSERT_EQ(oss.str(), expectResult);
239}
240
241HWTEST_F_L0(AssemblerAarch64Test, Branch)
242{
243    std::string expectResult("00000000:eb02003f \tcmp\tx1, x2\n"
244                             "00000004:54000080 \tb.eq\t0x14\n"
245                             "00000008:f100203f \tcmp\tx1, #8\n"
246                             "0000000c:5400004c \tb.gt\t0x14\n"
247                             "00000010:14000002 \tb\t0x18\n"
248                             "00000014:d2800140 \tmov\tx0, #10\n"
249                             "00000018:b27f03e0 \torr\tx0, xzr, #0x2\n");
250    AssemblerAarch64 masm(chunk_);
251    Label label1;
252    Label label2;
253    __ Cmp(Register(X1), Register(X2));
254    __ B(Condition::EQ, &label1);
255    __ Cmp(Register(X1), Immediate(8));
256    __ B(Condition::GT, &label1);
257    __ B(&label2);
258    __ Bind(&label1);
259    {
260        __ Mov(Register(X0), Immediate(0xa));
261    }
262    __ Bind(&label2);
263    {
264        __ Mov(Register(X0), Immediate(0x2));
265    }
266
267    std::ostringstream oss;
268    DisassembleChunk(TARGET_AARCH64, &masm, oss);
269    ASSERT_EQ(oss.str(), expectResult);
270}
271
272HWTEST_F_L0(AssemblerAarch64Test, Loop)
273{
274    std::string expectResult("00000000:7100005f \tcmp\tw2, #0\n"
275                             "00000004:540000e0 \tb.eq\t0x20\n"
276                             "00000008:51000442 \tsub\tw2, w2, #1\n"
277                             "0000000c:8b224c84 \tadd\tx4, x4, w2, uxtw #3\n"
278                             "00000010:f85f8485 \tldr\tx5, [x4], #-8\n"
279                             "00000014:f81f8fe5 \tstr\tx5, [sp, #-8]!\n"
280                             "00000018:51000442 \tsub\tw2, w2, #1\n"
281                             "0000001c:54ffffa5 \tb.pl\t0x10\n"
282                             "00000020:d2800140 \tmov\tx0, #10\n");
283    AssemblerAarch64 masm(chunk_);
284    Label label1;
285    Label labelLoop;
286    Register count(X2, W);
287    Register base(X4);
288    Register temp(X5);
289    __ Cmp(count, Immediate(0));
290    __ B(Condition::EQ, &label1);
291    __ Add(count, count, Immediate(-1));
292    __ Add(base, base, Operand(count, UXTW, 3));
293    __ Bind(&labelLoop);
294    {
295        __ Ldr(temp, MemoryOperand(base, -8, POSTINDEX));
296        __ Str(temp, MemoryOperand(Register(SP), -8, PREINDEX));
297        __ Add(count, count, Immediate(-1));
298        __ B(Condition::PL, &labelLoop);
299    }
300    __ Bind(&label1);
301    {
302        __ Mov(Register(X0), Immediate(0xa));
303    }
304    std::ostringstream oss;
305    DisassembleChunk(TARGET_AARCH64, &masm, oss);
306    ASSERT_EQ(oss.str(), expectResult);
307}
308
309HWTEST_F_L0(AssemblerAarch64Test, TbzAndCbz)
310{
311    std::string expectResult("00000000:36780001 \ttbz\tw1, #15, 0x0\n"
312                             "00000004:b60000c2 \ttbz\tx2, #32, 0x1c\n"
313                             "00000008:372800c2 \ttbnz\tw2, #5, 0x20\n"
314                             "0000000c:34000063 \tcbz\tw3, 0x18\n"
315                             "00000010:b5000064 \tcbnz\tx4, 0x1c\n"
316                             "00000014:b4000065 \tcbz\tx5, 0x20\n"
317                             "00000018:b24003e0 \torr\tx0, xzr, #0x1\n"
318                             "0000001c:b27f03e0 \torr\tx0, xzr, #0x2\n"
319                             "00000020:b24007e0 \torr\tx0, xzr, #0x3\n");
320    AssemblerAarch64 masm(chunk_);
321    Label label1;
322    Label label2;
323    Label label3;
324    __ Tbz(Register(X1), 15, &label1);
325    __ Tbz(Register(X2), 32,  &label2);
326    __ Tbnz(Register(X2), 5,  &label3);
327    __ Cbz(Register(X3).W(), &label1);
328    __ Cbnz(Register(X4), &label2);
329    __ Cbz(Register(X5), &label3);
330    __ Bind(&label1);
331    {
332        __ Mov(Register(X0), Immediate(0x1));
333    }
334    __ Bind(&label2);
335    {
336        __ Mov(Register(X0), Immediate(0x2));
337    }
338    __ Bind(&label3);
339    {
340        __ Mov(Register(X0), Immediate(0x3));
341    }
342    std::ostringstream oss;
343    DisassembleChunk(TARGET_AARCH64, &masm, oss);
344    ASSERT_EQ(oss.str(), expectResult);
345}
346
347HWTEST_F_L0(AssemblerAarch64Test, Call)
348{
349    std::string expectResult("00000000:b24003e0 \torr\tx0, xzr, #0x1\n"
350                             "00000004:b27f03e1 \torr\tx1, xzr, #0x2\n"
351                             "00000008:b24007e2 \torr\tx2, xzr, #0x3\n"
352                             "0000000c:97fffffd \tbl\t0x0\n"
353                             "00000010:d63f0040 \tblr\tx2\n");
354    AssemblerAarch64 masm(chunk_);
355    Label label1;
356    __ Bind(&label1);
357    {
358        __ Mov(Register(X0), Immediate(0x1));
359        __ Mov(Register(X1), Immediate(0x2));
360        __ Mov(Register(X2), Immediate(0x3));
361        __ Bl(&label1);
362        __ Blr(Register(X2));
363    }
364    std::ostringstream oss;
365    DisassembleChunk(TARGET_AARCH64, &masm, oss);
366    ASSERT_EQ(oss.str(), expectResult);
367}
368
369HWTEST_F_L0(AssemblerAarch64Test, RetAndBrk)
370{
371    std::string expectResult("00000000:d65f03c0 \tret\n"
372                             "00000004:d65f0280 \tret\tx20\n"
373                             "00000008:d4200000 \tbrk\t#0\n");
374    AssemblerAarch64 masm(chunk_);
375    __ Ret();
376    __ Ret(Register(X20));
377    __ Brk(Immediate(0));
378
379    std::ostringstream oss;
380    DisassembleChunk(TARGET_AARCH64, &masm, oss);
381    ASSERT_EQ(oss.str(), expectResult);
382}
383#undef __
384}
385