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