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 
24 namespace panda::ecmascript::aarch64 {
25 using namespace panda::ecmascript::base;
26 static const uint64_t HWORD_MASK = 0xFFFF;
27 
Create(uint64_t imm, int width)28 LogicalImmediate 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 
Ldp(const Register &rt, const Register &rt2, const MemoryOperand &operand)90 void 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 
Stp(const Register &rt, const Register &rt2, const MemoryOperand &operand)124 void 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 
Ldp(const VectorRegister &vt, const VectorRegister &vt2, const MemoryOperand &operand)158 void 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 
Stp(const VectorRegister &vt, const VectorRegister &vt2, const MemoryOperand &operand)204 void 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 
GetOpcFromScale(Scale scale, bool ispair)250 uint32_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 
Ldr(const Register &rt, const MemoryOperand &operand, Scale scale)277 void 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 
Ldr(const Register &rt, const MemoryOperand &operand)303 void AssemblerAarch64::Ldr(const Register &rt, const MemoryOperand &operand)
304 {
305     Ldr(rt, operand, Scale::Q);
306 }
307 
Ldrh(const Register &rt, const MemoryOperand &operand)308 void AssemblerAarch64::Ldrh(const Register &rt, const MemoryOperand &operand)
309 {
310     ASSERT(rt.IsW());
311     Ldr(rt, operand, Scale::H);
312 }
313 
Ldrb(const Register &rt, const MemoryOperand &operand)314 void AssemblerAarch64::Ldrb(const Register &rt, const MemoryOperand &operand)
315 {
316     ASSERT(rt.IsW());
317     Ldr(rt, operand, Scale::B);
318 }
319 
Str(const Register &rt, const MemoryOperand &operand)320 void 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 
Ldur(const Register &rt, const MemoryOperand &operand)357 void 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 
Stur(const Register &rt, const MemoryOperand &operand)369 void 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 
Mov(const Register &rd, const Immediate &imm)381 void 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 
Mov(const Register &rd, const Register &rm)459 void 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.
IsStartHWord(uint64_t hWord)471 static 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.
IsEndHWord(uint64_t hWord)482 static 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.
UpdateImm(uint64_t imm, unsigned idx, bool clear)491 static 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 
TrySequenceOfOnes(const Register &rd, uint64_t imm)503 bool 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 
TryReplicateHWords(const Register &rd, uint64_t imm)570 bool 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 
EmitMovInstruct(const Register &rd, uint64_t imm, unsigned int allOneHWords, unsigned int allZeroHWords)620 void 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 
Movz(const Register &rd, uint64_t imm, int shift)658 void AssemblerAarch64::Movz(const Register &rd, uint64_t imm, int shift)
659 {
660     MovWide(MoveOpCode::MOVZ, rd, imm, shift);
661 }
662 
Movk(const Register &rd, uint64_t imm, int shift)663 void AssemblerAarch64::Movk(const Register &rd, uint64_t imm, int shift)
664 {
665     MovWide(MoveOpCode::MOVK, rd, imm, shift);
666 }
667 
Movn(const Register &rd, uint64_t imm, int shift)668 void AssemblerAarch64::Movn(const Register &rd, uint64_t imm, int shift)
669 {
670     MovWide(MoveOpCode::MOVN, rd, imm, shift);
671 }
672 
MovWide(uint32_t op, const Register &rd, uint64_t imm, int shift)673 void 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 
Orr(const Register &rd, const Register &rn, const LogicalImmediate &imm)682 void AssemblerAarch64::Orr(const Register &rd, const Register &rn, const LogicalImmediate &imm)
683 {
684     BitWiseOpImm(ORR_Imm, rd, rn, imm.Value());
685 }
686 
And(const Register &rd, const Register &rn, const LogicalImmediate &imm)687 void AssemblerAarch64::And(const Register &rd, const Register &rn, const LogicalImmediate &imm)
688 {
689     BitWiseOpImm(AND_Imm, rd, rn, imm.Value());
690 }
691 
Ands(const Register &rd, const Register &rn, const LogicalImmediate &imm)692 void AssemblerAarch64::Ands(const Register &rd, const Register &rn, const LogicalImmediate &imm)
693 {
694     BitWiseOpImm(ANDS_Imm, rd, rn, imm.Value());
695 }
696 
Orr(const Register &rd, const Register &rn, const Operand &operand)697 void 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 
And(const Register &rd, const Register &rn, const Operand &operand)703 void 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 
Ands(const Register &rd, const Register &rn, const Operand &operand)709 void 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 
BitWiseOpImm(BitwiseOpCode op, const Register &rd, const Register &rn, uint64_t imm)715 void 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 
BitWiseOpShift(BitwiseOpCode op, const Register &rd, const Register &rn, const Operand &operand)721 void 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 
Lsl(const Register &rd, const Register &rn, const Register &rm)730 void 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 
Lsr(const Register &rd, const Register &rn, const Register &rm)736 void 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 
Ubfm(const Register &rd, const Register &rn, unsigned immr, unsigned imms)742 void 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 
Bfm(const Register &rd, const Register &rn, unsigned immr, unsigned imms)752 void 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 
Lsr(const Register &rd, const Register &rn, unsigned shift)762 void 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 
Add(const Register &rd, const Register &rn, const Operand &operand)777 void 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 
Adds(const Register &rd, const Register &rn, const Operand &operand)795 void 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 
Sub(const Register &rd, const Register &rn, const Operand &operand)808 void 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 
Subs(const Register &rd, const Register &rn, const Operand &operand)826 void 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 
IsAddSubImm(uint64_t imm)839 bool 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 
AddSubImm(AddSubOpCode op, const Register &rd, const Register &rn, bool setFlags, uint64_t imm)852 void 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 
AddSubReg(AddSubOpCode op, const Register &rd, const Register &rn, bool setFlags, const Operand &operand)870 void 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 
Cmp(const Register &rd, const Operand &operand)892 void AssemblerAarch64::Cmp(const Register &rd, const Operand &operand)
893 {
894     Subs(Register(Zero, rd.GetType()), rd, operand);
895 }
896 
CMov(const Register &rd, const Register &rn, const Operand &operand, Condition cond)897 void 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 
B(Label *label)905 void 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 
B(int32_t imm)913 void AssemblerAarch64::B(int32_t imm)
914 {
915     uint32_t code = BranchOpCode::Branch | ((imm << BRANCH_Imm26_LOWBITS) & BRANCH_Imm26_MASK);
916     EmitU32(code);
917 }
918 
Br(const Register &rn)919 void AssemblerAarch64::Br(const Register &rn)
920 {
921     uint32_t code = BranchOpCode::BR | Rn(rn.GetId());
922     EmitU32(code);
923 }
924 
Bl(Label *label)925 void 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 
Bl(int32_t imm)933 void AssemblerAarch64::Bl(int32_t imm)
934 {
935     uint32_t code = CallOpCode::BL | ((imm << BRANCH_Imm26_LOWBITS) & BRANCH_Imm26_MASK);
936     EmitU32(code);
937 }
938 
Blr(const Register &rn)939 void AssemblerAarch64::Blr(const Register &rn)
940 {
941     ASSERT(!rn.IsW());
942     uint32_t code = CallOpCode::BLR | Rn(rn.GetId());
943     EmitU32(code);
944 }
945 
B(Condition cond, Label *label)946 void 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 
B(Condition cond, int32_t imm)954 void AssemblerAarch64::B(Condition cond, int32_t imm)
955 {
956     uint32_t code = BranchOpCode::BranchCond | BranchImm19(imm) | cond;
957     EmitU32(code);
958 }
959 
Cbz(const Register &rt, Label *label)960 void 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 
Cbnz(const Register &rt, Label *label)968 void 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 
Cbz(const Register &rt, int32_t imm)976 void 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 
Cbnz(const Register &rt, int32_t imm)982 void 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 
Tbz(const Register &rt, int32_t bitPos, Label *label)988 void 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 
Tbz(const Register &rt, int32_t bitPos, int32_t imm)996 void 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 
Tbnz(const Register &rt, int32_t bitPos, Label *label)1005 void 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 
Tbnz(const Register &rt, int32_t bitPos, int32_t imm)1013 void 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 
Tst(const Register& rn, const Operand& operand)1022 void AssemblerAarch64::Tst(const Register& rn, const Operand& operand)
1023 {
1024     Ands(Register(Zero, rn.GetType()), rn, operand);
1025 }
1026 
Tst(const Register &rn, const LogicalImmediate &imm)1027 void AssemblerAarch64::Tst(const Register &rn, const LogicalImmediate &imm)
1028 {
1029     Ands(Register(Zero, rn.GetType()), rn, imm);
1030 }
1031 
LinkAndGetInstOffsetToLabel(Label *label)1032 int32_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 
Bind(Label *target)1048 void 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 
GetLinkOffsetFromBranchInst(int32_t pos)1067 int32_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 
ImmBranch(uint32_t branchCode)1075 int32_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 
SetRealOffsetToBranchInst(uint32_t linkPos, int32_t disp)1108 void 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 
Ret()1130 void AssemblerAarch64::Ret()
1131 {
1132     Ret(Register(X30));
1133 }
1134 
Ret(const Register &rn)1135 void AssemblerAarch64::Ret(const Register &rn)
1136 {
1137     uint32_t code = RetOpCode::Ret | Rn(rn.GetId());
1138     EmitU32(code);
1139 }
1140 
Brk(const Immediate &imm)1141 void 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 
GetImmOfLdr(const MemoryOperand &operand, Scale scale, bool isRegX)1149 uint64_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 
GetOpcodeOfLdr(const MemoryOperand &operand, Scale scale)1167 uint64_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 
GetShiftOfLdr(const MemoryOperand &operand, Scale scale, bool isRegX)1230 uint32_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