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 <cstdint> 17#include <map> 18 19#include "ecmascript/compiler/assembler/aarch64/assembler_aarch64.h" 20 21#include "ecmascript/base/bit_helper.h" 22#include "ecmascript/ecma_macros.h" 23 24namespace panda::ecmascript::aarch64 { 25using namespace panda::ecmascript::base; 26static const uint64_t HWORD_MASK = 0xFFFF; 27 28LogicalImmediate LogicalImmediate::Create(uint64_t imm, int width) 29{ 30 if ((imm == 0ULL) || (imm == ~0ULL) || 31 ((width != RegXSize) && (((imm >> width) != 0) || (imm == (~0ULL >> (RegXSize - width)))))) { 32 return LogicalImmediate(InvalidLogicalImmediate); 33 } 34 35 // First, determine the element size. 36 unsigned int size = static_cast<uint32_t>(width); 37 do { 38 size /= 2; // 2: Divide by 2 39 uint64_t mask = (1ULL << size) - 1; 40 41 if ((imm & mask) != ((imm >> size) & mask)) { 42 size *= 2; // 2: Multiply by 2 43 break; 44 } 45 } while (size > 2); // 2: Greater than 2 46 47 // Second, determine the rotation to make the element be: 0^m 1^n. 48 unsigned int cto = 0; 49 unsigned int i = 0; 50 uint64_t mask = ((uint64_t)-1LL) >> (RegXSize - size); 51 imm &= mask; 52 53 if (IsShiftedMask_64(imm)) { 54 i = CountTrailingZeros64(imm); 55 ASSERT_PRINT(i < RegXSize, "undefined behavior"); 56 cto = CountTrailingOnes64(imm >> i); 57 } else { 58 imm |= ~mask; 59 if (!IsShiftedMask_64(~imm)) { 60 return LogicalImmediate(InvalidLogicalImmediate); 61 } 62 63 uint32_t clo = CountLeadingOnes64(imm); 64 i = static_cast<uint32_t>(RegXSize) - clo; 65 cto = clo + CountTrailingOnes64(imm) - (static_cast<uint32_t>(RegXSize) - size); 66 } 67 68 // Encode in Immr the number of RORs it would take to get *from* 0^m 1^n 69 // to our target value, where I is the number of RORs to go the opposite 70 // direction. 71 ASSERT_PRINT(size > i, "i should be smaller than element size"); 72 unsigned immr = (size - i) & (size - 1); 73 74 // If size has a 1 in the n'th bit, create a value that has zeroes in 75 // bits [0, n] and ones above that. 76 uint64_t nImms = ~(size - 1) << 1; 77 78 // Or the CTO value into the low bits, which must be below the Nth bit 79 // bit mentioned above. 80 ASSERT(cto > 0); 81 nImms |= (cto - 1); 82 83 // Extract the seventh bit and toggle it to create the N field. 84 // 6 means the topmost bit in nImms 85 unsigned int n = ((nImms >> 6) & 1) ^ 1; 86 return LogicalImmediate((n << BITWISE_OP_N_LOWBITS) | (immr << BITWISE_OP_Immr_LOWBITS) | 87 ((nImms << BITWISE_OP_Imms_LOWBITS) & BITWISE_OP_Imms_MASK)); 88} 89 90void AssemblerAarch64::Ldp(const Register &rt, const Register &rt2, const MemoryOperand &operand) 91{ 92 uint32_t op = 0; 93 if (operand.IsImmediateOffset()) { 94 switch (operand.GetAddrMode()) { 95 case OFFSET: 96 op = LoadStorePairOpCode::LDP_Offset; 97 break; 98 case PREINDEX: 99 op = LoadStorePairOpCode::LDP_Pre; 100 break; 101 case POSTINDEX: 102 op = LoadStorePairOpCode::LDP_Post; 103 break; 104 default: 105 LOG_ECMA(FATAL) << "this branch is unreachable"; 106 UNREACHABLE(); 107 } 108 bool sf = !rt.IsW(); 109 uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value()); 110 if (sf) { 111 imm >>= 3; // 3: 64 RegSise, imm/8 to remove trailing zeros 112 } else { 113 imm >>= 2; // 2: 32 RegSise, imm/4 to remove trailing zeros 114 } 115 uint32_t instructionCode = Sf(sf) | op | LoadAndStorePairImm(imm) | Rt2(rt2.GetId()) | 116 Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId()); 117 EmitU32(instructionCode); 118 return; 119 } 120 LOG_ECMA(FATAL) << "this branch is unreachable"; 121 UNREACHABLE(); 122} 123 124void AssemblerAarch64::Stp(const Register &rt, const Register &rt2, const MemoryOperand &operand) 125{ 126 uint32_t op = 0; 127 if (operand.IsImmediateOffset()) { 128 switch (operand.GetAddrMode()) { 129 case OFFSET: 130 op = LoadStorePairOpCode::STP_Offset; 131 break; 132 case PREINDEX: 133 op = LoadStorePairOpCode::STP_Pre; 134 break; 135 case POSTINDEX: 136 op = LoadStorePairOpCode::STP_Post; 137 break; 138 default: 139 LOG_ECMA(FATAL) << "this branch is unreachable"; 140 UNREACHABLE(); 141 } 142 bool sf = !rt.IsW(); 143 uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value()); 144 if (sf) { 145 imm >>= 3; // 3: 64 RegSise, imm/8 to remove trailing zeros 146 } else { 147 imm >>= 2; // 2: 32 RegSise, imm/4 to remove trailing zeros 148 } 149 uint32_t instructionCode = Sf(sf) | op | LoadAndStorePairImm(imm) | Rt2(rt2.GetId()) | 150 Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId()); 151 EmitU32(instructionCode); 152 return; 153 } 154 LOG_ECMA(FATAL) << "this branch is unreachable"; 155 UNREACHABLE(); 156} 157 158void AssemblerAarch64::Ldp(const VectorRegister &vt, const VectorRegister &vt2, const MemoryOperand &operand) 159{ 160 uint32_t op = 0; 161 if (operand.IsImmediateOffset()) { 162 switch (operand.GetAddrMode()) { 163 case OFFSET: 164 op = LoadStorePairOpCode::LDP_V_Offset; 165 break; 166 case PREINDEX: 167 op = LoadStorePairOpCode::LDP_V_Pre; 168 break; 169 case POSTINDEX: 170 op = LoadStorePairOpCode::LDP_V_Post; 171 break; 172 default: 173 LOG_ECMA(FATAL) << "this branch is unreachable"; 174 UNREACHABLE(); 175 } 176 uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value()); 177 switch (vt.GetScale()) { 178 case S: 179 // 2 : 2 means remove trailing zeros 180 imm >>= 2; 181 break; 182 case D: 183 // 3 : 3 means remove trailing zeros 184 imm >>= 3; 185 break; 186 case Q: 187 // 4 : 4 means remove trailing zeros 188 imm >>= 4; 189 break; 190 default: 191 LOG_ECMA(FATAL) << "this branch is unreachable"; 192 UNREACHABLE(); 193 } 194 uint32_t opc = GetOpcFromScale(vt.GetScale(), true); 195 uint32_t instructionCode = opc | op | LoadAndStorePairImm(imm) | Rt2(vt2.GetId()) | 196 Rn(operand.GetRegBase().GetId()) | Rt(vt.GetId()); 197 EmitU32(instructionCode); 198 return; 199 } 200 LOG_ECMA(FATAL) << "this branch is unreachable"; 201 UNREACHABLE(); 202} 203 204void AssemblerAarch64::Stp(const VectorRegister &vt, const VectorRegister &vt2, const MemoryOperand &operand) 205{ 206 uint32_t op = 0; 207 if (operand.IsImmediateOffset()) { 208 switch (operand.GetAddrMode()) { 209 case OFFSET: 210 op = LoadStorePairOpCode::STP_V_Offset; 211 break; 212 case PREINDEX: 213 op = LoadStorePairOpCode::STP_V_Pre; 214 break; 215 case POSTINDEX: 216 op = LoadStorePairOpCode::STP_V_Post; 217 break; 218 default: 219 LOG_ECMA(FATAL) << "this branch is unreachable"; 220 UNREACHABLE(); 221 } 222 uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value()); 223 switch (vt.GetScale()) { 224 case S: 225 // 2 : 2 means remove trailing zeros 226 imm >>= 2; 227 break; 228 case D: 229 // 3 : 3 means remove trailing zeros 230 imm >>= 3; 231 break; 232 case Q: 233 // 4 : 4 means remove trailing zeros 234 imm >>= 4; 235 break; 236 default: 237 LOG_ECMA(FATAL) << "this branch is unreachable"; 238 UNREACHABLE(); 239 } 240 uint32_t opc = GetOpcFromScale(vt.GetScale(), true); 241 uint32_t instructionCode = opc | op | LoadAndStorePairImm(imm) | Rt2(vt2.GetId()) | 242 Rn(operand.GetRegBase().GetId()) | Rt(vt.GetId()); 243 EmitU32(instructionCode); 244 return; 245 } 246 LOG_ECMA(FATAL) << "this branch is unreachable"; 247 UNREACHABLE(); 248} 249 250uint32_t AssemblerAarch64::GetOpcFromScale(Scale scale, bool ispair) 251{ 252 uint32_t opc = 0; 253 switch (scale) { 254 case Scale::B: 255 case Scale::H: 256 ASSERT(!ispair); 257 opc = 1; 258 break; 259 case Scale::S: 260 opc = ispair ? 0 : 1; 261 break; 262 case Scale::D: 263 opc = 1; 264 break; 265 case Scale::Q: 266 // 3 : means opc bit is 11 267 opc = ispair ? 1 : 3; 268 break; 269 default: 270 LOG_ECMA(FATAL) << "this branch is unreachable"; 271 UNREACHABLE(); 272 } 273 274 return (opc << LDP_STP_Opc_LOWBITS) & LDP_STP_Opc_MASK; 275} 276 277void AssemblerAarch64::Ldr(const Register &rt, const MemoryOperand &operand, Scale scale) 278{ 279 bool regX = !rt.IsW(); 280 uint32_t op = GetOpcodeOfLdr(operand, scale); 281 if (operand.IsImmediateOffset()) { 282 uint64_t imm = GetImmOfLdr(operand, scale, regX); 283 bool isSigned = operand.GetAddrMode() != AddrMode::OFFSET; 284 // 30: 30bit indicate the size of LDR Reg, and Ldrb and Ldrh do not need it 285 uint32_t instructionCode = ((regX && (scale == Scale::Q)) << 30) | op | LoadAndStoreImm(imm, isSigned) | 286 Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId()); 287 EmitU32(instructionCode); 288 } else { 289 ASSERT(operand.GetExtendOption() != Extend::NO_EXTEND); 290 uint32_t shift = GetShiftOfLdr(operand, scale, regX); 291 Register rm = operand.GetRegisterOffset(); 292 Register rn = operand.GetRegBase(); 293 uint32_t extendField = 294 (operand.GetExtendOption() << LDR_STR_Extend_LOWBITS) & LDR_STR_Extend_MASK; 295 uint32_t shiftField = (shift << LDR_STR_S_LOWBITS) & LDR_STR_S_MASK; 296 // 30: 30bit indicate the size of LDR Reg, and Ldrb and Ldrh do not need it 297 uint32_t instructionCode = ((regX && (scale == Scale::Q)) << 30) | op | Rm(rm.GetId()) | 298 extendField | shiftField | Rn(rn.GetId()) | Rt(rt.GetId()); 299 EmitU32(instructionCode); 300 } 301} 302 303void AssemblerAarch64::Ldr(const Register &rt, const MemoryOperand &operand) 304{ 305 Ldr(rt, operand, Scale::Q); 306} 307 308void AssemblerAarch64::Ldrh(const Register &rt, const MemoryOperand &operand) 309{ 310 ASSERT(rt.IsW()); 311 Ldr(rt, operand, Scale::H); 312} 313 314void AssemblerAarch64::Ldrb(const Register &rt, const MemoryOperand &operand) 315{ 316 ASSERT(rt.IsW()); 317 Ldr(rt, operand, Scale::B); 318} 319 320void AssemblerAarch64::Str(const Register &rt, const MemoryOperand &operand) 321{ 322 uint32_t op = 0; 323 bool regX = !rt.IsW(); 324 bool isSigned = true; 325 uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value()); 326 if (operand.IsImmediateOffset()) { 327 switch (operand.GetAddrMode()) { 328 case OFFSET: 329 op = LoadStoreOpCode::STR_Offset; 330 if (regX) { 331 imm >>= 3; // 3: 64 RegSise, imm/8 to remove trailing zeros 332 } else { 333 imm >>= 2; // 2: 32 RegSise, imm/4 to remove trailing zeros 334 } 335 isSigned = false; 336 break; 337 case PREINDEX: 338 op = LoadStoreOpCode::STR_Pre; 339 break; 340 case POSTINDEX: 341 op = LoadStoreOpCode::STR_Post; 342 break; 343 default: 344 LOG_ECMA(FATAL) << "this branch is unreachable"; 345 UNREACHABLE(); 346 } 347 // 30: 30bit indicate the size of LDR Reg 348 uint32_t instructionCode = (regX << 30) | op | LoadAndStoreImm(imm, isSigned) | 349 Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId()); 350 EmitU32(instructionCode); 351 return; 352 } 353 LOG_ECMA(FATAL) << "this branch is unreachable"; 354 UNREACHABLE(); 355} 356 357void AssemblerAarch64::Ldur(const Register &rt, const MemoryOperand &operand) 358{ 359 bool regX = !rt.IsW(); 360 uint32_t op = LDUR_Offset; 361 ASSERT(operand.IsImmediateOffset()); 362 uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value()); 363 // 30: 30bit indicate the size of LDUR Reg 364 uint32_t instructionCode = (regX << 30) | op | LoadAndStoreImm(imm, true) | 365 Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId()); 366 EmitU32(instructionCode); 367} 368 369void AssemblerAarch64::Stur(const Register &rt, const MemoryOperand &operand) 370{ 371 bool regX = !rt.IsW(); 372 uint32_t op = STUR_Offset; 373 ASSERT(operand.IsImmediateOffset()); 374 uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value()); 375 // 30: 30bit indicate the size of LDUR Reg 376 uint32_t instructionCode = (regX << 30) | op | LoadAndStoreImm(imm, true) | 377 Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId()); 378 EmitU32(instructionCode); 379} 380 381void AssemblerAarch64::Mov(const Register &rd, const Immediate &imm) 382{ 383 ASSERT_PRINT(!rd.IsSp(), "sp can't load immediate, please use add instruction"); 384 const unsigned int HWORDSIZE = 16; 385 uint64_t immValue = static_cast<uint64_t>(imm.Value()); 386 unsigned int allOneHalfWords = 0; 387 unsigned int allZeroHalfWords = 0; 388 unsigned int regSize = rd.IsW() ? RegWSize : RegXSize; 389 unsigned int halfWords = regSize / HWORDSIZE; 390 391 for (unsigned int shift = 0; shift < regSize; shift += HWORDSIZE) { 392 const unsigned int halfWord = (immValue >> shift) & HWORD_MASK; 393 if (halfWord == HWORD_MASK) { 394 allOneHalfWords++; 395 } else if (halfWord == 0) { 396 allZeroHalfWords++; 397 } 398 } 399 // use movz/movn over ORR. 400 if (((halfWords - allOneHalfWords) <= 1) && ((halfWords - allZeroHalfWords) <= 1)) { 401 EmitMovInstruct(rd, immValue, allOneHalfWords, allZeroHalfWords); 402 return; 403 } 404 // Try a single ORR. 405 uint64_t realImm = immValue << (RegXSize - regSize) >> (RegXSize - regSize); 406 LogicalImmediate orrImm = LogicalImmediate::Create(realImm, regSize); 407 if (orrImm.IsValid()) { 408 Orr(rd, Register(Zero), orrImm); 409 return; 410 } 411 // 2: One to up three instruction sequence. 412 if (allOneHalfWords >= (halfWords - 2) || allZeroHalfWords >= (halfWords - 2)) { 413 EmitMovInstruct(rd, immValue, allOneHalfWords, allZeroHalfWords); 414 return; 415 } 416 ASSERT_PRINT(regSize == RegXSize, "all 32-bit Immediate will be transformed with a MOVZ/MOVK pair"); 417 418 for (unsigned int shift = 0; shift < regSize; shift += HWORDSIZE) { 419 uint64_t shiftedMask = (HWORD_MASK << shift); 420 uint64_t zeroChunk = realImm & ~shiftedMask; 421 uint64_t oneChunk = realImm | shiftedMask; 422 uint64_t rotatedImm = (realImm << 32) | (realImm >> 32); 423 uint64_t replicateChunk = zeroChunk | (rotatedImm & shiftedMask); 424 LogicalImmediate zeroImm = LogicalImmediate::Create(zeroChunk, regSize); 425 LogicalImmediate oneImm = LogicalImmediate::Create(oneChunk, regSize); 426 LogicalImmediate replicateImm = LogicalImmediate::Create(replicateChunk, regSize); 427 if (!zeroImm.IsValid() && !oneImm.IsValid() && !replicateImm.IsValid()) { 428 continue; 429 } 430 431 if (zeroImm.IsValid()) { 432 Orr(rd, Register(Zero), zeroImm); 433 } else if (oneImm.IsValid()) { 434 Orr(rd, Register(Zero), oneImm); 435 } else { 436 Orr(rd, Register(Zero), replicateImm); 437 } 438 const uint64_t movkImm = (realImm & shiftedMask) >> shift; 439 Movk(rd, movkImm, shift); 440 return; 441 } 442 443 if (allOneHalfWords || allZeroHalfWords) { 444 EmitMovInstruct(rd, immValue, allOneHalfWords, allZeroHalfWords); 445 return; 446 } 447 448 if (regSize == RegXSize && TryReplicateHWords(rd, realImm)) { 449 return; 450 } 451 452 if (regSize == RegXSize && TrySequenceOfOnes(rd, realImm)) { 453 return; 454 } 455 EmitMovInstruct(rd, immValue, allOneHalfWords, allZeroHalfWords); 456 return; 457} 458 459void AssemblerAarch64::Mov(const Register &rd, const Register &rm) 460{ 461 if (rd.IsSp() || rm.IsSp()) { 462 Add(rd, rm, Operand(Immediate(0))); 463 } else { 464 Orr(rd, Register(Zero), Operand(rm)); 465 } 466} 467 468/// Check whether this chunk matches the pattern '1...0...'. This pattern 469/// starts a contiguous sequence of ones if we look at the bits from the LSB 470/// towards the MSB. 471static bool IsStartHWord(uint64_t hWord) 472{ 473 if (hWord == 0 || hWord == std::numeric_limits<uint64_t>::max()) { 474 return false; 475 } 476 return IsMask_64(~hWord); 477} 478 479/// Check whether this chunk matches the pattern '0...1...' This pattern 480/// ends a contiguous sequence of ones if we look at the bits from the LSB 481/// towards the MSB. 482static bool IsEndHWord(uint64_t hWord) 483{ 484 if (hWord == 0 || hWord == std::numeric_limits<uint64_t>::max()) { 485 return false; 486 } 487 return IsMask_64(hWord); 488} 489 490/// Clear or set all bits in the chunk at the given index. 491static uint64_t UpdateImm(uint64_t imm, unsigned idx, bool clear) 492{ 493 if (clear) { 494 // Clear chunk in the immediate. 495 imm &= ~(HWORD_MASK << idx); 496 } else { 497 // Set all bits in the immediate for the particular chunk. 498 imm |= HWORD_MASK << idx; 499 } 500 return imm; 501} 502 503bool AssemblerAarch64::TrySequenceOfOnes(const Register &rd, uint64_t imm) 504{ 505 const int HWORDSIZE = 16; 506 int startIdx = -1; 507 int endIdx = -1; 508 // Try to find the chunks which start/end a contiguous sequence of ones. 509 for (int shift = 0; shift < RegXSize; shift += HWORDSIZE) { 510 int64_t himm = (imm >> shift) & HWORD_MASK; 511 // Sign extend the 16-bit chunk to 64-bit. 512 // 48 : 48 means RegXSize - HWORDSIZE 513 himm = (himm << 48) >> 48; 514 515 if (IsStartHWord(himm)) { 516 startIdx = shift; 517 } else if (IsEndHWord(static_cast<uint64_t>(himm))) { 518 endIdx = shift; 519 } 520 } 521 // Early exit in case we can't find a start/end chunk. 522 if (startIdx == -1 || endIdx == -1) { 523 return false; 524 } 525 // Outside of the contiguous sequence of ones everything needs to be zero. 526 uint64_t outside = 0; 527 // Chunks between the start and end chunk need to have all their bits set. 528 uint64_t inside = HWORD_MASK; 529 530 // If our contiguous sequence of ones wraps around from the MSB into the LSB, 531 // just swap indices and pretend we are materializing a contiguous sequence 532 // of zeros surrounded by a contiguous sequence of ones. 533 if (startIdx > endIdx) { 534 std::swap(startIdx, endIdx); 535 std::swap(outside, inside); 536 } 537 538 uint64_t orrImm = imm; 539 int firstMovkShift = -1; 540 int secondMovkShift = -1; 541 for (int shift = 0; shift < RegXSize; shift += HWORDSIZE) { 542 uint64_t himm = (imm >> shift) & HWORD_MASK; 543 // Check whether we are looking at a chunk which is not part of the 544 // contiguous sequence of ones. 545 if ((shift < startIdx || endIdx < shift) && himm != outside) { 546 orrImm = UpdateImm(orrImm, shift, outside == 0); 547 if (firstMovkShift == -1) { 548 firstMovkShift = shift; 549 } else { 550 secondMovkShift = shift; 551 } 552 } else if (shift > startIdx && shift < endIdx && himm != inside) { 553 orrImm = UpdateImm(orrImm, shift, outside == 0); 554 if (firstMovkShift == -1) { 555 firstMovkShift = shift; 556 } else { 557 secondMovkShift = shift; 558 } 559 } 560 } 561 ASSERT_PRINT(firstMovkShift != -1, "constant materializable with single orr!"); 562 Orr(rd, rd, LogicalImmediate::Create(orrImm, RegXSize)); 563 Movk(rd, (imm >> firstMovkShift) & HWORD_MASK, firstMovkShift); 564 if (secondMovkShift != -1) { 565 Movk(rd, (imm >> secondMovkShift) & HWORD_MASK, secondMovkShift); 566 } 567 return true; 568} 569 570bool AssemblerAarch64::TryReplicateHWords(const Register &rd, uint64_t imm) 571{ 572 const int HWORDSIZE = 16; 573 std::map<uint64_t, int> repeatMaps; 574 for (int idx = 0; idx < RegXSize; idx += HWORDSIZE) { 575 uint64_t halfWord = (imm >> idx) & HWORD_MASK; 576 if (repeatMaps.find(halfWord) != repeatMaps.end()) { 577 repeatMaps[halfWord] += 1; 578 } else { 579 repeatMaps[halfWord] = 1; 580 } 581 } 582 for (auto iter : repeatMaps) { 583 const uint64_t hImm = iter.first; 584 const int count = iter.second; 585 uint64_t repeatImm = hImm | (hImm << 16) | (hImm << 32) | (hImm << 48); 586 LogicalImmediate orrImm = LogicalImmediate::Create(repeatImm, 64); 587 // if orrImm not valid, repeat count can't be 2 or 3, it can't be simplified with orr. 588 if ((count != 2 && count != 3) || orrImm.IsValid()) { 589 continue; 590 } 591 Orr(rd, rd, orrImm); 592 int shift = 0; 593 uint64_t imm16 = 0; 594 // Find the first chunk not materialized with the ORR instruction. 595 for (; shift < RegXSize; shift += HWORDSIZE) { 596 imm16 = (imm >> shift) & HWORD_MASK; 597 if (imm16 != hImm) { 598 break; 599 } 600 } 601 // Create the first MOVK instruction. 602 Movk(rd, imm16, shift); 603 // 3 : 3 means repeat 3 times, Imm encode has been done. 604 if (count == 3) { 605 return true; 606 } 607 // Find the remaining chunk which needs to be materialized. 608 for (shift += HWORDSIZE; shift < RegXSize; shift += HWORDSIZE) { 609 imm16 = (imm >> shift) & HWORD_MASK; 610 if (imm16 != hImm) { 611 break; 612 } 613 } 614 Movk(rd, imm16, shift); 615 return true; 616 } 617 return false; 618} 619 620void AssemblerAarch64::EmitMovInstruct(const Register &rd, uint64_t imm, 621 unsigned int allOneHWords, unsigned int allZeroHWords) 622{ 623 bool isNeg = false; 624 if (allOneHWords > allZeroHWords) { 625 isNeg = true; 626 imm = ~imm; 627 } 628 int firstshift = 0; // LSL amount for high bits with MOVZ/MOVN 629 int lastshift = 0; // LSL amount for last MOVK 630 if (imm != 0) { 631 int lz = static_cast<int>(CountLeadingZeros64(imm)); 632 int tz = static_cast<int>(CountTrailingZeros64(imm)); 633 firstshift = (tz / 16) * 16; // 16 : 16 means the operand of MOVK/N/Z is 16 bits Immediate 634 // 63 : 63 means the topmost bits of RegXSize 635 lastshift = ((63 - lz) / 16) * 16; // 16 : 16 means the operand of MOVK/N/Z is 16 bits Immediate 636 } 637 uint64_t imm16 = (imm >> firstshift) & HWORD_MASK; 638 if (isNeg) { 639 Movn(rd, imm16, firstshift); 640 imm = ~imm; 641 } else { 642 Movz(rd, imm16, firstshift); 643 } 644 if (firstshift == lastshift) { 645 return; 646 } 647 while (firstshift < lastshift) { 648 firstshift += 16; // 16 : 16 means the operand of MOVK is 16 bits Immediate 649 imm16 = (imm >> firstshift) & HWORD_MASK; 650 if (imm16 == (isNeg ? HWORD_MASK : 0)) { 651 // skip movk because initial value is already set correctly. 652 continue; 653 } 654 Movk(rd, imm16, firstshift); 655 } 656} 657 658void AssemblerAarch64::Movz(const Register &rd, uint64_t imm, int shift) 659{ 660 MovWide(MoveOpCode::MOVZ, rd, imm, shift); 661} 662 663void AssemblerAarch64::Movk(const Register &rd, uint64_t imm, int shift) 664{ 665 MovWide(MoveOpCode::MOVK, rd, imm, shift); 666} 667 668void AssemblerAarch64::Movn(const Register &rd, uint64_t imm, int shift) 669{ 670 MovWide(MoveOpCode::MOVN, rd, imm, shift); 671} 672 673void AssemblerAarch64::MovWide(uint32_t op, const Register &rd, uint64_t imm, int shift) 674{ 675 uint32_t imm_field = (imm << MOV_WIDE_Imm16_LOWBITS) & MOV_WIDE_Imm16_MASK; 676 uint32_t hw_field = ((shift / 16) << MOV_WIDE_Hw_LOWBITS) & MOV_WIDE_Hw_MASK; 677 uint32_t code = Sf(!rd.IsW()) | op | imm_field | hw_field | Rd(rd.GetId()); 678 EmitU32(code); 679} 680 681 682void AssemblerAarch64::Orr(const Register &rd, const Register &rn, const LogicalImmediate &imm) 683{ 684 BitWiseOpImm(ORR_Imm, rd, rn, imm.Value()); 685} 686 687void AssemblerAarch64::And(const Register &rd, const Register &rn, const LogicalImmediate &imm) 688{ 689 BitWiseOpImm(AND_Imm, rd, rn, imm.Value()); 690} 691 692void AssemblerAarch64::Ands(const Register &rd, const Register &rn, const LogicalImmediate &imm) 693{ 694 BitWiseOpImm(ANDS_Imm, rd, rn, imm.Value()); 695} 696 697void AssemblerAarch64::Orr(const Register &rd, const Register &rn, const Operand &operand) 698{ 699 ASSERT(operand.IsShifted()); 700 BitWiseOpShift(ORR_Shift, rd, rn, operand); 701} 702 703void AssemblerAarch64::And(const Register &rd, const Register &rn, const Operand &operand) 704{ 705 ASSERT(operand.IsShifted()); 706 BitWiseOpShift(AND_Shift, rd, rn, operand); 707} 708 709void AssemblerAarch64::Ands(const Register &rd, const Register &rn, const Operand &operand) 710{ 711 ASSERT(operand.IsShifted()); 712 BitWiseOpShift(ANDS_Shift, rd, rn, operand); 713} 714 715void AssemblerAarch64::BitWiseOpImm(BitwiseOpCode op, const Register &rd, const Register &rn, uint64_t imm) 716{ 717 uint32_t code = Sf(!rd.IsW()) | op | imm | Rn(rn.GetId()) | Rd(rd.GetId()); 718 EmitU32(code); 719} 720 721void AssemblerAarch64::BitWiseOpShift(BitwiseOpCode op, const Register &rd, const Register &rn, const Operand &operand) 722{ 723 uint32_t shift_field = (operand.GetShiftOption() << BITWISE_OP_Shift_LOWBITS) & BITWISE_OP_Shift_MASK; 724 uint32_t shift_amount = (operand.GetShiftAmount() << BITWISE_OP_ShiftAmount_LOWBITS) & BITWISE_OP_ShiftAmount_MASK; 725 uint32_t code = Sf(!rd.IsW()) | op | shift_field | Rm(operand.Reg().GetId()) | 726 shift_amount | Rn(rn.GetId()) | Rd(rd.GetId()); 727 EmitU32(code); 728} 729 730void AssemblerAarch64::Lsl(const Register &rd, const Register &rn, const Register &rm) 731{ 732 uint32_t code = Sf(!rd.IsW()) | LSL_Reg | Rm(rm.GetId()) | Rn(rn.GetId()) | Rd(rd.GetId()); 733 EmitU32(code); 734} 735 736void AssemblerAarch64::Lsr(const Register &rd, const Register &rn, const Register &rm) 737{ 738 uint32_t code = Sf(!rd.IsW()) | LSR_Reg | Rm(rm.GetId()) | Rn(rn.GetId()) | Rd(rd.GetId()); 739 EmitU32(code); 740} 741 742void AssemblerAarch64::Ubfm(const Register &rd, const Register &rn, unsigned immr, unsigned imms) 743{ 744 bool sf = !rd.IsW(); 745 uint32_t n = (sf << BITWISE_OP_N_LOWBITS) & BITWISE_OP_N_MASK; 746 uint32_t immr_field = (immr << BITWISE_OP_Immr_LOWBITS) & BITWISE_OP_Immr_MASK; 747 uint32_t imms_field = (imms << BITWISE_OP_Imms_LOWBITS) & BITWISE_OP_Imms_MASK; 748 uint32_t code = Sf(sf) | UBFM | n | immr_field | imms_field | Rn(rn.GetId()) | Rd(rd.GetId()); 749 EmitU32(code); 750} 751 752void AssemblerAarch64::Bfm(const Register &rd, const Register &rn, unsigned immr, unsigned imms) 753{ 754 bool sf = !rd.IsW(); 755 uint32_t n = (sf << BITWISE_OP_N_LOWBITS) & BITWISE_OP_N_MASK; 756 uint32_t immr_field = (immr << BITWISE_OP_Immr_LOWBITS) & BITWISE_OP_Immr_MASK; 757 uint32_t imms_field = (imms << BITWISE_OP_Imms_LOWBITS) & BITWISE_OP_Imms_MASK; 758 uint32_t code = Sf(sf) | BFM | n | immr_field | imms_field | Rn(rn.GetId()) | Rd(rd.GetId()); 759 EmitU32(code); 760} 761 762void AssemblerAarch64::Lsr(const Register &rd, const Register &rn, unsigned shift) 763{ 764 unsigned imms = 0; 765 if (rd.IsW()) { 766 imms = 31; // 31 : 31 32-bit variant Applies when sf == 0 && N == 0 && imms == 011111 767 // LSR <Wd>, <Wn>, #<shift> is equivalent to UBFM <Wd>, <Wn>, #<shift>, #31 768 // and is always the preferred disassembly 769 } else { 770 imms = 63; // 63 : 63 64-bit variant Applies when sf == 1 && N == 1 && imms == 111111 771 // LSR <Xd>, <Xn>, #<shift> is equivalent to UBFM <Xd>, <Xn>, #<shift>, #63 772 // and is always the preferred disassembly 773 } 774 Ubfm(rd, rn, shift, imms); 775} 776 777void AssemblerAarch64::Add(const Register &rd, const Register &rn, const Operand &operand) 778{ 779 if (operand.IsImmediate()) { 780 int64_t imm = static_cast<int64_t>(operand.ImmediateValue()); 781 if (imm < 0) { 782 AddSubImm(SUB_Imm, rd, rn, false, -1 * imm); 783 } else { 784 AddSubImm(ADD_Imm, rd, rn, false, imm); 785 } 786 } else { 787 if (operand.IsShifted()) { 788 AddSubReg(ADD_Shift, rd, rn, false, operand); 789 } else { 790 AddSubReg(ADD_Extend, rd, rn, false, operand); 791 } 792 } 793} 794 795void AssemblerAarch64::Adds(const Register &rd, const Register &rn, const Operand &operand) 796{ 797 if (operand.IsImmediate()) { 798 AddSubImm(ADD_Imm, rd, rn, true, operand.ImmediateValue()); 799 } else { 800 if (operand.IsShifted()) { 801 AddSubReg(ADD_Shift, rd, rn, true, operand); 802 } else { 803 AddSubReg(ADD_Extend, rd, rn, true, operand); 804 } 805 } 806} 807 808void AssemblerAarch64::Sub(const Register &rd, const Register &rn, const Operand &operand) 809{ 810 if (operand.IsImmediate()) { 811 int64_t imm = static_cast<int64_t>(operand.ImmediateValue()); 812 if (imm < 0) { 813 AddSubImm(ADD_Imm, rd, rn, false, -1 * imm); 814 } else { 815 AddSubImm(SUB_Imm, rd, rn, false, imm); 816 } 817 } else { 818 if (operand.IsShifted()) { 819 AddSubReg(SUB_Shift, rd, rn, false, operand); 820 } else { 821 AddSubReg(SUB_Extend, rd, rn, false, operand); 822 } 823 } 824} 825 826void AssemblerAarch64::Subs(const Register &rd, const Register &rn, const Operand &operand) 827{ 828 if (operand.IsImmediate()) { 829 AddSubImm(SUB_Imm, rd, rn, true, operand.ImmediateValue()); 830 } else { 831 if (operand.IsShifted()) { 832 AddSubReg(SUB_Shift, rd, rn, true, operand); 833 } else { 834 AddSubReg(SUB_Extend, rd, rn, true, operand); 835 } 836 } 837} 838 839bool AssemblerAarch64::IsAddSubImm(uint64_t imm) 840{ 841 const uint64_t IMM12_MASK = (1 << ADD_SUB_Imm12_WIDTH) - 1; 842 if (imm <= IMM12_MASK) { 843 return true; 844 } 845 846 if (((imm & IMM12_MASK) == 0) && ((imm & ~IMM12_MASK) <= IMM12_MASK)) { 847 return true; 848 } 849 return false; 850} 851 852void AssemblerAarch64::AddSubImm(AddSubOpCode op, const Register &rd, const Register &rn, bool setFlags, uint64_t imm) 853{ 854 ASSERT(IsAddSubImm(imm)); 855 uint32_t shift = 0; 856 const uint64_t IMM12_MASK = (1 << ADD_SUB_Imm12_WIDTH) - 1; 857 uint64_t imm12 = imm & (~IMM12_MASK); 858 if (imm12 != 0) { 859 shift = 1; 860 } else { 861 imm12 = imm; 862 } 863 uint32_t flags_field = ((setFlags ? 1 : 0) << ADD_SUB_S_LOWBITS) & ADD_SUB_S_MASK; 864 uint32_t imm_field = (imm12 << ADD_SUB_Imm12_LOWBITS) & ADD_SUB_Imm12_MASK; 865 uint32_t shift_field = (shift << ADD_SUB_Sh_LOWBITS) & ADD_SUB_Sh_MASK; 866 uint32_t code = Sf(!rd.IsW()) | op | flags_field | shift_field | imm_field | Rd(rd.GetId()) | Rn(rn.GetId()); 867 EmitU32(code); 868} 869 870void AssemblerAarch64::AddSubReg(AddSubOpCode op, const Register &rd, const Register &rn, 871 bool setFlags, const Operand &operand) 872{ 873 uint32_t flags_field = ((setFlags ? 1 : 0) << ADD_SUB_S_LOWBITS) & ADD_SUB_S_MASK; 874 uint32_t code = 0; 875 if (operand.IsShifted()) { 876 uint32_t shift_field = ((operand.GetShiftOption()) << ADD_SUB_Shift_LOWBITS) & ADD_SUB_Shift_MASK; 877 uint32_t shift_amount = ((operand.GetShiftAmount()) << ADD_SUB_ShiftAmount_LOWBITS) & ADD_SUB_ShiftAmount_MASK; 878 ASSERT((op == ADD_Shift) | (op == SUB_Shift)); 879 code = Sf(!rd.IsW()) | op | flags_field | shift_field | Rm(operand.Reg().GetId()) | 880 shift_amount | Rn(rn.GetId()) | Rd(rd.GetId()); 881 } else { 882 ASSERT((op == ADD_Extend) | (op == SUB_Extend)); 883 uint32_t extend_field = 884 (operand.GetExtendOption() << ADD_SUB_ExtendOption_LOWBITS) & ADD_SUB_ExtendOption_MASK; 885 uint32_t extend_shift = (operand.GetShiftAmount() << ADD_SUB_ExtendShift_LOWBITS) & ADD_SUB_ExtendShift_MASK; 886 code = Sf(!rd.IsW()) | op | flags_field | Rm(operand.Reg().GetId()) | extend_field | 887 extend_shift | Rn(rn.GetId()) | Rd(rd.GetId()); 888 } 889 EmitU32(code); 890} 891 892void AssemblerAarch64::Cmp(const Register &rd, const Operand &operand) 893{ 894 Subs(Register(Zero, rd.GetType()), rd, operand); 895} 896 897void AssemblerAarch64::CMov(const Register &rd, const Register &rn, const Operand &operand, Condition cond) 898{ 899 ASSERT(!operand.IsImmediate()); 900 uint32_t cond_field = (cond << CSEL_Cond_LOWBITS) & CSEL_Cond_MASK; 901 uint32_t code = Sf(!rd.IsW()) | CSEL | Rm(operand.Reg().GetId()) | cond_field | Rn(rn.GetId()) | Rd(rd.GetId()); 902 EmitU32(code); 903} 904 905void AssemblerAarch64::B(Label *label) 906{ 907 int32_t offsetImm = LinkAndGetInstOffsetToLabel(label); 908 // 2 : 2 means 4 bytes aligned. 909 offsetImm >>= 2; 910 B(offsetImm); 911} 912 913void AssemblerAarch64::B(int32_t imm) 914{ 915 uint32_t code = BranchOpCode::Branch | ((imm << BRANCH_Imm26_LOWBITS) & BRANCH_Imm26_MASK); 916 EmitU32(code); 917} 918 919void AssemblerAarch64::Br(const Register &rn) 920{ 921 uint32_t code = BranchOpCode::BR | Rn(rn.GetId()); 922 EmitU32(code); 923} 924 925void AssemblerAarch64::Bl(Label *label) 926{ 927 int32_t offsetImm = LinkAndGetInstOffsetToLabel(label); 928 // 2 : 2 means 4 bytes aligned. 929 offsetImm >>= 2; 930 Bl(offsetImm); 931} 932 933void AssemblerAarch64::Bl(int32_t imm) 934{ 935 uint32_t code = CallOpCode::BL | ((imm << BRANCH_Imm26_LOWBITS) & BRANCH_Imm26_MASK); 936 EmitU32(code); 937} 938 939void AssemblerAarch64::Blr(const Register &rn) 940{ 941 ASSERT(!rn.IsW()); 942 uint32_t code = CallOpCode::BLR | Rn(rn.GetId()); 943 EmitU32(code); 944} 945 946void AssemblerAarch64::B(Condition cond, Label *label) 947{ 948 int32_t offsetImm = LinkAndGetInstOffsetToLabel(label); 949 // 2 : 2 means 4 bytes aligned. 950 offsetImm >>= 2; 951 B(cond, offsetImm); 952} 953 954void AssemblerAarch64::B(Condition cond, int32_t imm) 955{ 956 uint32_t code = BranchOpCode::BranchCond | BranchImm19(imm) | cond; 957 EmitU32(code); 958} 959 960void AssemblerAarch64::Cbz(const Register &rt, Label *label) 961{ 962 int32_t offsetImm = LinkAndGetInstOffsetToLabel(label); 963 // 2 : 2 means 4 bytes aligned. 964 offsetImm >>= 2; 965 Cbz(rt, offsetImm); 966} 967 968void AssemblerAarch64::Cbnz(const Register &rt, Label *label) 969{ 970 int32_t offsetImm = LinkAndGetInstOffsetToLabel(label); 971 // 2 : 2 means 4 bytes aligned. 972 offsetImm >>= 2; 973 Cbnz(rt, offsetImm); 974} 975 976void AssemblerAarch64::Cbz(const Register &rt, int32_t imm) 977{ 978 uint32_t code = Sf(!rt.IsW()) | BranchOpCode::CBZ | BranchImm19(imm) | rt.GetId(); 979 EmitU32(code); 980} 981 982void AssemblerAarch64::Cbnz(const Register &rt, int32_t imm) 983{ 984 uint32_t code = Sf(!rt.IsW()) | BranchOpCode::CBNZ | BranchImm19(imm) | rt.GetId(); 985 EmitU32(code); 986} 987 988void AssemblerAarch64::Tbz(const Register &rt, int32_t bitPos, Label *label) 989{ 990 int32_t offsetImm = LinkAndGetInstOffsetToLabel(label); 991 // 2 : 2 means 4 bytes aligned. 992 offsetImm >>= 2; 993 Tbz(rt, bitPos, offsetImm); 994} 995 996void AssemblerAarch64::Tbz(const Register &rt, int32_t bitPos, int32_t imm) 997{ 998 uint32_t b5 = (bitPos << (BRANCH_B5_LOWBITS - 5)) & BRANCH_B5_MASK; 999 uint32_t b40 = (bitPos << BRANCH_B40_LOWBITS) & BRANCH_B40_MASK; 1000 uint32_t imm14 = (imm << BRANCH_Imm14_LOWBITS) & BRANCH_Imm14_MASK; 1001 uint32_t code = b5 | BranchOpCode::TBZ | b40 | imm14 | rt.GetId(); 1002 EmitU32(code); 1003} 1004 1005void AssemblerAarch64::Tbnz(const Register &rt, int32_t bitPos, Label *label) 1006{ 1007 int32_t offsetImm = LinkAndGetInstOffsetToLabel(label); 1008 // 2 : 2 means 4 bytes aligned. 1009 offsetImm >>= 2; 1010 Tbnz(rt, bitPos, offsetImm); 1011} 1012 1013void AssemblerAarch64::Tbnz(const Register &rt, int32_t bitPos, int32_t imm) 1014{ 1015 uint32_t b5 = (bitPos << (BRANCH_B5_LOWBITS - 5)) & BRANCH_B5_MASK; 1016 uint32_t b40 = (bitPos << BRANCH_B40_LOWBITS) & BRANCH_B40_MASK; 1017 uint32_t imm14 = (imm <<BRANCH_Imm14_LOWBITS) & BRANCH_Imm14_MASK; 1018 uint32_t code = b5 | BranchOpCode::TBNZ | b40 | imm14 | rt.GetId(); 1019 EmitU32(code); 1020} 1021 1022void AssemblerAarch64::Tst(const Register& rn, const Operand& operand) 1023{ 1024 Ands(Register(Zero, rn.GetType()), rn, operand); 1025} 1026 1027void AssemblerAarch64::Tst(const Register &rn, const LogicalImmediate &imm) 1028{ 1029 Ands(Register(Zero, rn.GetType()), rn, imm); 1030} 1031 1032int32_t AssemblerAarch64::LinkAndGetInstOffsetToLabel(Label *label) 1033{ 1034 int32_t offset = 0; 1035 if (label->IsBound()) { 1036 offset = static_cast<int32_t>(label->GetPos() - GetCurrentPosition()); 1037 } else { 1038 if (label->IsLinked()) { 1039 offset = static_cast<int32_t>(label->GetLinkedPos() - GetCurrentPosition()); 1040 } else { 1041 offset = 0; 1042 } 1043 label->LinkTo(GetCurrentPosition()); 1044 } 1045 return offset; 1046} 1047 1048void AssemblerAarch64::Bind(Label *target) 1049{ 1050 size_t pos = GetCurrentPosition(); 1051 ASSERT(!target->IsBound()); 1052 if (target->IsLinked()) { 1053 uint32_t linkPos = target->GetLinkedPos(); 1054 while (linkPos != 0) { 1055 int32_t offset = GetLinkOffsetFromBranchInst(linkPos); 1056 int32_t disp = static_cast<int32_t>(pos - linkPos); 1057 SetRealOffsetToBranchInst(linkPos, disp); 1058 if (offset == 0) { 1059 break; 1060 } 1061 linkPos = linkPos + offset; 1062 } 1063 } 1064 target->BindTo(pos); 1065} 1066 1067int32_t AssemblerAarch64::GetLinkOffsetFromBranchInst(int32_t pos) 1068{ 1069 uint32_t branchCode = GetU32(pos); 1070 // 2 : 2 means 4 bytes aligned. 1071 int32_t immOffSet = ImmBranch(branchCode) << 2; 1072 return immOffSet; 1073} 1074 1075int32_t AssemblerAarch64::ImmBranch(uint32_t branchCode) 1076{ 1077 int32_t immOffset = 0; 1078 if ((branchCode & BranchFMask) == BranchOpCode::Branch) { 1079 immOffset = (branchCode & BRANCH_Imm26_MASK) >> BRANCH_Imm26_LOWBITS; 1080 if (immOffset & (1 << (BRANCH_Imm26_WIDTH - 1))) { 1081 // 31 : 31 means topmost bits of instruction "uint32_t" 1082 immOffset |= ((1 << (31 - BRANCH_Imm26_WIDTH)) - 1) << BRANCH_Imm26_WIDTH; 1083 } 1084 } else if ((branchCode & BranchCondFMask) == BranchOpCode::BranchCond) { 1085 immOffset = (branchCode & BRANCH_Imm19_MASK) >> BRANCH_Imm19_LOWBITS; 1086 if (immOffset & (1 << (BRANCH_Imm19_WIDTH - 1))) { 1087 // 31 : 31 means topmost bits of instruction "uint32_t" 1088 immOffset |= ((1 << (31 - BRANCH_Imm19_WIDTH)) - 1) << BRANCH_Imm19_WIDTH; 1089 } 1090 } else if ((branchCode & BranchCompareFMask) == BranchOpCode::CBZ) { 1091 immOffset = (branchCode & BRANCH_Imm19_MASK) >> BRANCH_Imm19_LOWBITS; 1092 if (immOffset & (1 << (BRANCH_Imm19_WIDTH - 1))) { 1093 // 31 : 31 means topmost bits of instruction "uint32_t" 1094 immOffset |= ((1 << (31 - BRANCH_Imm19_WIDTH)) - 1) << BRANCH_Imm19_WIDTH; 1095 } 1096 } else if ((branchCode & BranchTestFMask) == BranchOpCode::TBZ) { 1097 immOffset = (branchCode & BRANCH_Imm14_MASK) >> BRANCH_Imm14_LOWBITS; 1098 if (immOffset & (1 << (BRANCH_Imm14_WIDTH - 1))) { 1099 // 31 : 31 means topmost bits of instruction "uint32_t" 1100 immOffset |= ((1 << (31 - BRANCH_Imm14_WIDTH)) - 1) << BRANCH_Imm14_WIDTH; 1101 } 1102 } else { 1103 UNREACHABLE(); 1104 } 1105 return immOffset; 1106} 1107 1108void AssemblerAarch64::SetRealOffsetToBranchInst(uint32_t linkPos, int32_t disp) 1109{ 1110 uint32_t branchCode = GetU32(linkPos); 1111 // 2 : 2 means 4 bytes aligned. 1112 uint32_t immOffset = disp >> 2; 1113 1114 if ((branchCode & BranchFMask) == BranchOpCode::Branch) { 1115 branchCode &= ~BRANCH_Imm26_MASK; 1116 branchCode |= (immOffset << BRANCH_Imm26_LOWBITS) & BRANCH_Imm26_MASK; 1117 } else if ((branchCode & BranchCondFMask) == BranchOpCode::BranchCond) { 1118 branchCode &= ~BRANCH_Imm19_MASK; 1119 branchCode |= (immOffset << BRANCH_Imm19_LOWBITS) & BRANCH_Imm19_MASK; 1120 } else if ((branchCode & BranchCompareFMask) == BranchOpCode::CBZ) { 1121 branchCode &= ~BRANCH_Imm19_MASK; 1122 branchCode |= (immOffset << BRANCH_Imm19_LOWBITS) & BRANCH_Imm19_MASK; 1123 } else if ((branchCode & BranchTestFMask) == BranchOpCode::TBZ) { 1124 branchCode &= ~BRANCH_Imm14_MASK; 1125 branchCode |= (immOffset << BRANCH_Imm14_LOWBITS) & BRANCH_Imm14_MASK; 1126 } 1127 PutI32(linkPos, branchCode); 1128} 1129 1130void AssemblerAarch64::Ret() 1131{ 1132 Ret(Register(X30)); 1133} 1134 1135void AssemblerAarch64::Ret(const Register &rn) 1136{ 1137 uint32_t code = RetOpCode::Ret | Rn(rn.GetId()); 1138 EmitU32(code); 1139} 1140 1141void AssemblerAarch64::Brk(const Immediate &imm) 1142{ 1143 uint32_t brk_number_field = 1144 (static_cast<uint32_t>(imm.Value()) << BRK_Imm16_LOWBITS) & BRK_Imm16_MASK; 1145 uint32_t code = BRKImm | brk_number_field; 1146 EmitU32(code); 1147} 1148 1149uint64_t AssemblerAarch64::GetImmOfLdr(const MemoryOperand &operand, Scale scale, bool isRegX) 1150{ 1151 ASSERT(operand.IsImmediateOffset()); 1152 uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value()); 1153 if (operand.GetAddrMode() == OFFSET) { 1154 if (scale == Scale::H) { 1155 imm >>= 1; 1156 } else if (scale == Scale::Q) { 1157 if (isRegX) { 1158 imm >>= 3; // 3: 64 RegSise, imm/8 to remove trailing zeros 1159 } else { 1160 imm >>= 2; // 2: 32 RegSise, imm/4 to remove trailing zeros 1161 } 1162 } 1163 } 1164 return imm; 1165} 1166 1167uint64_t AssemblerAarch64::GetOpcodeOfLdr(const MemoryOperand &operand, Scale scale) 1168{ 1169 uint32_t op = 0; 1170 if (operand.IsImmediateOffset()) { 1171 switch (operand.GetAddrMode()) { 1172 case OFFSET: { 1173 if (scale == Scale::B) { 1174 op = LoadStoreOpCode::LDRB_Offset; 1175 } else if (scale == Scale::H) { 1176 op = LoadStoreOpCode::LDRH_Offset; 1177 } else if (scale == Scale::Q) { 1178 op = LoadStoreOpCode::LDR_Offset; 1179 } else { 1180 LOG_ECMA(FATAL) << "this branch is unreachable"; 1181 UNREACHABLE(); 1182 } 1183 break; 1184 } 1185 case PREINDEX: { 1186 if (scale == Scale::B) { 1187 op = LoadStoreOpCode::LDRB_Pre; 1188 } else if (scale == Scale::H) { 1189 op = LoadStoreOpCode::LDRH_Pre; 1190 } else if (scale == Scale::Q) { 1191 op = LoadStoreOpCode::LDR_Pre; 1192 } else { 1193 LOG_ECMA(FATAL) << "this branch is unreachable"; 1194 UNREACHABLE(); 1195 } 1196 break; 1197 } 1198 case POSTINDEX: { 1199 if (scale == Scale::B) { 1200 op = LoadStoreOpCode::LDRB_Post; 1201 } else if (scale == Scale::H) { 1202 op = LoadStoreOpCode::LDRH_Post; 1203 } else if (scale == Scale::Q) { 1204 op = LoadStoreOpCode::LDR_Post; 1205 } else { 1206 LOG_ECMA(FATAL) << "this branch is unreachable"; 1207 UNREACHABLE(); 1208 } 1209 break; 1210 } 1211 default: 1212 LOG_ECMA(FATAL) << "this branch is unreachable"; 1213 UNREACHABLE(); 1214 } 1215 } else { 1216 if (scale == Scale::B) { 1217 op = LoadStoreOpCode::LDRB_Register; 1218 } else if (scale == Scale::H) { 1219 op = LoadStoreOpCode::LDRH_Register; 1220 } else if (scale == Scale::Q) { 1221 op = LoadStoreOpCode::LDR_Register; 1222 } else { 1223 LOG_ECMA(FATAL) << "this branch is unreachable"; 1224 UNREACHABLE(); 1225 } 1226 } 1227 return op; 1228} 1229 1230uint32_t AssemblerAarch64::GetShiftOfLdr(const MemoryOperand &operand, Scale scale, bool isRegX) 1231{ 1232 uint32_t shift = 0; 1233 if (scale == Scale::B) { 1234 shift = operand.GetShiftOption() != Shift::NO_SHIFT; 1235 } else if (scale == Scale::H) { 1236 shift = operand.GetShiftAmount(); 1237 ASSERT(shift == 0 || shift == 1); 1238 shift = (shift == 0) ? 0 : 1; 1239 } else if (scale == Scale::Q) { 1240 shift = operand.GetShiftAmount(); 1241 if (isRegX) { 1242 // 3 : 3 means address aligned with 8bytes 1243 ASSERT(shift == 0 || shift == 3); 1244 } else { 1245 // 2 : 2 means address aligned with 4bytes 1246 ASSERT(shift == 0 || shift == 2); 1247 } 1248 shift = (shift == 0) ? 0 : 1; 1249 } 1250 return shift; 1251} 1252} // namespace panda::ecmascript::aarch64 1253