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