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(TARGET_AARCH64);
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(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
DisassembleChunk(const char * triple,Assembler * assemlber,std::ostream & os)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.
HWTEST_F_L0(AssemblerAarch64Test,Mov)123 HWTEST_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
HWTEST_F_L0(AssemblerAarch64Test,MovReg)145 HWTEST_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
HWTEST_F_L0(AssemblerAarch64Test,LdpStp)159 HWTEST_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
HWTEST_F_L0(AssemblerAarch64Test,LdrStr)176 HWTEST_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
HWTEST_F_L0(AssemblerAarch64Test,AddSub)207 HWTEST_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
HWTEST_F_L0(AssemblerAarch64Test,CMP)228 HWTEST_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
HWTEST_F_L0(AssemblerAarch64Test,Branch)241 HWTEST_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
HWTEST_F_L0(AssemblerAarch64Test,Loop)272 HWTEST_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
HWTEST_F_L0(AssemblerAarch64Test,TbzAndCbz)309 HWTEST_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
HWTEST_F_L0(AssemblerAarch64Test,Call)347 HWTEST_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
HWTEST_F_L0(AssemblerAarch64Test,RetAndBrk)369 HWTEST_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