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 35namespace panda::test { 36using namespace panda::ecmascript; 37using namespace panda::ecmascript::x64; 38 39class AssemblerX64Test : public testing::Test { 40public: 41 static void SetUpTestCase() 42 { 43 GTEST_LOG_(INFO) << "SetUpTestCase"; 44 } 45 46 static void TearDownTestCase() 47 { 48 GTEST_LOG_(INFO) << "TearDownCase"; 49 } 50 51 void SetUp() override 52 { 53 InitializeLLVM(TARGET_X64); 54 TestHelper::CreateEcmaVMWithScope(instance, thread, scope); 55 chunk_ = thread->GetEcmaVM()->GetChunk(); 56 } 57 58 void TearDown() override 59 { 60 TestHelper::DestroyEcmaVMWithScope(instance, scope); 61 } 62 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 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 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. 129HWTEST_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 178HWTEST_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 259HWTEST_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 321HWTEST_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 400HWTEST_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