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