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