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
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(TARGET_X64);
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(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
DisassembleChunk(const char * triple,Assembler * assemlber,std::ostream & os)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.
HWTEST_F_L0(AssemblerX64Test,Emit)129 HWTEST_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
HWTEST_F_L0(AssemblerX64Test,Emit1)178 HWTEST_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
HWTEST_F_L0(AssemblerX64Test,Emit2)259 HWTEST_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
HWTEST_F_L0(AssemblerX64Test,Emit3)321 HWTEST_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
HWTEST_F_L0(AssemblerX64Test,Emit4)400 HWTEST_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