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 31namespace panda::test { 32using namespace panda::ecmascript; 33using namespace panda::ecmascript::aarch64; 34class AssemblerAarch64Test : public testing::Test { 35public: 36 static void SetUpTestCase() 37 { 38 GTEST_LOG_(INFO) << "SetUpTestCase"; 39 } 40 41 static void TearDownTestCase() 42 { 43 GTEST_LOG_(INFO) << "TearDownCase"; 44 } 45 46 void SetUp() override 47 { 48 InitializeLLVM(TARGET_AARCH64); 49 TestHelper::CreateEcmaVMWithScope(instance, thread, scope); 50 chunk_ = thread->GetEcmaVM()->GetChunk(); 51 } 52 53 void TearDown() override 54 { 55 TestHelper::DestroyEcmaVMWithScope(instance, scope); 56 } 57 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 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 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. 123HWTEST_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 145HWTEST_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 159HWTEST_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 176HWTEST_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 207HWTEST_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 228HWTEST_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 241HWTEST_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 272HWTEST_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 309HWTEST_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 347HWTEST_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 369HWTEST_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