• 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; // 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     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                 LOG_ECMA(FATAL) << "this branch is unreachable";
105                 UNREACHABLE();
106         }
107         bool sf = !rt.IsW();
108         uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value());
109         if (sf) {
110             imm >>= 3;  // 3: 64 RegSise, imm/8 to remove trailing zeros
111         } else {
112             imm >>= 2;  // 2: 32 RegSise, imm/4 to remove trailing zeros
113         }
114         uint32_t instructionCode = Sf(sf) | op | LoadAndStorePairImm(imm) | Rt2(rt2.GetId()) |
115                                    Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId());
116         EmitU32(instructionCode);
117         return;
118     }
119     LOG_ECMA(FATAL) << "this branch is unreachable";
120     UNREACHABLE();
121 }
122 
Stp(const Register & rt,const Register & rt2,const MemoryOperand & operand)123 void AssemblerAarch64::Stp(const Register &rt, const Register &rt2, const MemoryOperand &operand)
124 {
125     uint32_t op = 0;
126     if (operand.IsImmediateOffset()) {
127         switch (operand.GetAddrMode()) {
128             case OFFSET:
129                 op = LoadStorePairOpCode::STP_Offset;
130                 break;
131             case PREINDEX:
132                 op = LoadStorePairOpCode::STP_Pre;
133                 break;
134             case POSTINDEX:
135                 op = LoadStorePairOpCode::STP_Post;
136                 break;
137             default:
138                 LOG_ECMA(FATAL) << "this branch is unreachable";
139                 UNREACHABLE();
140         }
141         bool sf = !rt.IsW();
142         uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value());
143         if (sf) {
144             imm >>= 3;  // 3: 64 RegSise, imm/8 to remove trailing zeros
145         } else {
146             imm >>= 2;  // 2: 32 RegSise, imm/4 to remove trailing zeros
147         }
148         uint32_t instructionCode = Sf(sf) | op | LoadAndStorePairImm(imm) | Rt2(rt2.GetId()) |
149                                    Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId());
150         EmitU32(instructionCode);
151         return;
152     }
153     LOG_ECMA(FATAL) << "this branch is unreachable";
154     UNREACHABLE();
155 }
156 
Ldp(const VectorRegister & vt,const VectorRegister & vt2,const MemoryOperand & operand)157 void AssemblerAarch64::Ldp(const VectorRegister &vt, const VectorRegister &vt2, const MemoryOperand &operand)
158 {
159     uint32_t op = 0;
160     if (operand.IsImmediateOffset()) {
161         switch (operand.GetAddrMode()) {
162             case OFFSET:
163                 op = LoadStorePairOpCode::LDP_V_Offset;
164                 break;
165             case PREINDEX:
166                 op = LoadStorePairOpCode::LDP_V_Pre;
167                 break;
168             case POSTINDEX:
169                 op = LoadStorePairOpCode::LDP_V_Post;
170                 break;
171             default:
172                 LOG_ECMA(FATAL) << "this branch is unreachable";
173                 UNREACHABLE();
174         }
175         uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value());
176         switch (vt.GetScale()) {
177             case S:
178                 // 2 : 2 means remove trailing zeros
179                 imm >>= 2;
180                 break;
181             case D:
182                 // 3 : 3 means remove trailing zeros
183                 imm >>= 3;
184                 break;
185             case Q:
186                 // 4 : 4 means remove trailing zeros
187                 imm >>= 4;
188                 break;
189             default:
190                 LOG_ECMA(FATAL) << "this branch is unreachable";
191                 UNREACHABLE();
192         }
193         uint32_t opc = GetOpcFromScale(vt.GetScale(), true);
194         uint32_t instructionCode = opc | op | LoadAndStorePairImm(imm) | Rt2(vt2.GetId()) |
195                                    Rn(operand.GetRegBase().GetId()) | Rt(vt.GetId());
196         EmitU32(instructionCode);
197         return;
198     }
199     LOG_ECMA(FATAL) << "this branch is unreachable";
200     UNREACHABLE();
201 }
202 
Stp(const VectorRegister & vt,const VectorRegister & vt2,const MemoryOperand & operand)203 void AssemblerAarch64::Stp(const VectorRegister &vt, const VectorRegister &vt2, const MemoryOperand &operand)
204 {
205     uint32_t op = 0;
206     if (operand.IsImmediateOffset()) {
207         switch (operand.GetAddrMode()) {
208             case OFFSET:
209                 op = LoadStorePairOpCode::STP_V_Offset;
210                 break;
211             case PREINDEX:
212                 op = LoadStorePairOpCode::STP_V_Pre;
213                 break;
214             case POSTINDEX:
215                 op = LoadStorePairOpCode::STP_V_Post;
216                 break;
217             default:
218                 LOG_ECMA(FATAL) << "this branch is unreachable";
219                 UNREACHABLE();
220         }
221         uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value());
222         switch (vt.GetScale()) {
223             case S:
224                 // 2 : 2 means remove trailing zeros
225                 imm >>= 2;
226                 break;
227             case D:
228                 // 3 : 3 means remove trailing zeros
229                 imm >>= 3;
230                 break;
231             case Q:
232                 // 4 : 4 means remove trailing zeros
233                 imm >>= 4;
234                 break;
235             default:
236                 LOG_ECMA(FATAL) << "this branch is unreachable";
237                 UNREACHABLE();
238         }
239         uint32_t opc = GetOpcFromScale(vt.GetScale(), true);
240         uint32_t instructionCode = opc | op | LoadAndStorePairImm(imm) | Rt2(vt2.GetId()) |
241                                    Rn(operand.GetRegBase().GetId()) | Rt(vt.GetId());
242         EmitU32(instructionCode);
243         return;
244     }
245     LOG_ECMA(FATAL) << "this branch is unreachable";
246     UNREACHABLE();
247 }
248 
GetOpcFromScale(Scale scale,bool ispair)249 uint32_t AssemblerAarch64::GetOpcFromScale(Scale scale, bool ispair)
250 {
251     uint32_t opc = 0;
252     switch (scale) {
253         case Scale::B:
254         case Scale::H:
255             ASSERT(!ispair);
256             opc = 1;
257             break;
258         case Scale::S:
259             opc = ispair ? 0 : 1;
260             break;
261         case Scale::D:
262             opc = 1;
263             break;
264         case Scale::Q:
265             // 3 : means opc bit is 11
266             opc = ispair ? 1 : 3;
267             break;
268         default:
269             LOG_ECMA(FATAL) << "this branch is unreachable";
270             UNREACHABLE();
271     }
272 
273     return (opc << LDP_STP_Opc_LOWBITS) & LDP_STP_Opc_MASK;
274 }
275 
Ldr(const Register & rt,const MemoryOperand & operand,Scale scale)276 void AssemblerAarch64::Ldr(const Register &rt, const MemoryOperand &operand, Scale scale)
277 {
278     bool regX = !rt.IsW();
279     uint32_t op = GetOpcodeOfLdr(operand, scale);
280     if (operand.IsImmediateOffset()) {
281         uint64_t imm = GetImmOfLdr(operand, scale, regX);
282         bool isSigned = operand.GetAddrMode() != AddrMode::OFFSET;
283         // 30: 30bit indicate the size of LDR Reg, and Ldrb and Ldrh do not need it
284         uint32_t instructionCode = ((regX && (scale == Scale::Q)) << 30) | op | LoadAndStoreImm(imm, isSigned) |
285                                     Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId());
286         EmitU32(instructionCode);
287     } else {
288         ASSERT(operand.GetExtendOption() != Extend::NO_EXTEND);
289         uint32_t shift = GetShiftOfLdr(operand, scale, regX);
290         Register rm = operand.GetRegisterOffset();
291         Register rn = operand.GetRegBase();
292         uint32_t extendField =
293             (operand.GetExtendOption() << LDR_STR_Extend_LOWBITS) & LDR_STR_Extend_MASK;
294         uint32_t shiftField = (shift << LDR_STR_S_LOWBITS) & LDR_STR_S_MASK;
295         // 30: 30bit indicate the size of LDR Reg, and Ldrb and Ldrh do not need it
296         uint32_t instructionCode = ((regX && (scale == Scale::Q)) << 30) | op | Rm(rm.GetId()) |
297                                     extendField | shiftField | Rn(rn.GetId()) | Rt(rt.GetId());
298         EmitU32(instructionCode);
299     }
300 }
301 
Ldr(const Register & rt,const MemoryOperand & operand)302 void AssemblerAarch64::Ldr(const Register &rt, const MemoryOperand &operand)
303 {
304     Ldr(rt, operand, Scale::Q);
305 }
306 
Ldrh(const Register & rt,const MemoryOperand & operand)307 void AssemblerAarch64::Ldrh(const Register &rt, const MemoryOperand &operand)
308 {
309     ASSERT(rt.IsW());
310     Ldr(rt, operand, Scale::H);
311 }
312 
Ldrb(const Register & rt,const MemoryOperand & operand)313 void AssemblerAarch64::Ldrb(const Register &rt, const MemoryOperand &operand)
314 {
315     ASSERT(rt.IsW());
316     Ldr(rt, operand, Scale::B);
317 }
318 
Str(const Register & rt,const MemoryOperand & operand)319 void AssemblerAarch64::Str(const Register &rt, const MemoryOperand &operand)
320 {
321     uint32_t op = 0;
322     bool regX = !rt.IsW();
323     bool isSigned = true;
324     uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value());
325     if (operand.IsImmediateOffset()) {
326         switch (operand.GetAddrMode()) {
327             case OFFSET:
328                 op = LoadStoreOpCode::STR_Offset;
329                 if (regX) {
330                     imm >>= 3;   // 3:  64 RegSise, imm/8 to remove trailing zeros
331                 } else {
332                     imm >>= 2;  // 2: 32 RegSise, imm/4 to remove trailing zeros
333                 }
334                 isSigned = false;
335                 break;
336             case PREINDEX:
337                 op = LoadStoreOpCode::STR_Pre;
338                 break;
339             case POSTINDEX:
340                 op = LoadStoreOpCode::STR_Post;
341                 break;
342             default:
343                 LOG_ECMA(FATAL) << "this branch is unreachable";
344                 UNREACHABLE();
345         }
346         // 30: 30bit indicate the size of LDR Reg
347         uint32_t instructionCode = (regX << 30) | op | LoadAndStoreImm(imm, isSigned) |
348                                    Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId());
349         EmitU32(instructionCode);
350         return;
351     }
352     LOG_ECMA(FATAL) << "this branch is unreachable";
353     UNREACHABLE();
354 }
355 
Ldur(const Register & rt,const MemoryOperand & operand)356 void AssemblerAarch64::Ldur(const Register &rt, const MemoryOperand &operand)
357 {
358     bool regX = !rt.IsW();
359     uint32_t op = LDUR_Offset;
360     ASSERT(operand.IsImmediateOffset());
361     uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value());
362     // 30: 30bit indicate the size of LDUR Reg
363     uint32_t instructionCode = (regX << 30) | op | LoadAndStoreImm(imm, true) |
364                                Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId());
365     EmitU32(instructionCode);
366 }
367 
Stur(const Register & rt,const MemoryOperand & operand)368 void AssemblerAarch64::Stur(const Register &rt, const MemoryOperand &operand)
369 {
370     bool regX = !rt.IsW();
371     uint32_t op = STUR_Offset;
372     ASSERT(operand.IsImmediateOffset());
373     uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value());
374     // 30: 30bit indicate the size of LDUR Reg
375     uint32_t instructionCode = (regX << 30) | op | LoadAndStoreImm(imm, true) |
376                                Rn(operand.GetRegBase().GetId()) | Rt(rt.GetId());
377     EmitU32(instructionCode);
378 }
379 
Mov(const Register & rd,const Immediate & imm)380 void AssemblerAarch64::Mov(const Register &rd, const Immediate &imm)
381 {
382     ASSERT_PRINT(!rd.IsSp(), "sp can't load immediate, please use add instruction");
383     const unsigned int HWORDSIZE = 16;
384     uint64_t immValue = static_cast<uint64_t>(imm.Value());
385     unsigned int allOneHalfWords = 0;
386     unsigned int allZeroHalfWords = 0;
387     unsigned int regSize = rd.IsW() ? RegWSize : RegXSize;
388     unsigned int halfWords = regSize / HWORDSIZE;
389 
390     for (unsigned int shift = 0; shift < regSize; shift += HWORDSIZE) {
391         const unsigned int halfWord = (immValue >> shift) & HWORD_MASK;
392         if (halfWord == HWORD_MASK) {
393             allOneHalfWords++;
394         } else if (halfWord == 0) {
395             allZeroHalfWords++;
396         }
397     }
398     // use movz/movn over ORR.
399     if (((halfWords - allOneHalfWords) <= 1) && ((halfWords - allZeroHalfWords) <= 1)) {
400         EmitMovInstruct(rd, immValue, allOneHalfWords, allZeroHalfWords);
401         return;
402     }
403     // Try a single ORR.
404     uint64_t realImm = immValue << (RegXSize - regSize) >> (RegXSize - regSize);
405     LogicalImmediate orrImm = LogicalImmediate::Create(realImm, regSize);
406     if (orrImm.IsValid()) {
407         Orr(rd, Register(Zero), orrImm);
408         return;
409     }
410     // One to up three instruction sequence.
411     if (allOneHalfWords >= (halfWords - 2) || // 2 : Must be greater than or equal to (halfWords -2)
412         allZeroHalfWords >= (halfWords - 2)) { // 2 : Must be greater than or equal to (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 
Lsr(const Register & rd,const Register & rn,unsigned shift)752 void AssemblerAarch64::Lsr(const Register &rd, const Register &rn, unsigned shift)
753 {
754     unsigned imms = 0;
755     if (rd.IsW()) {
756         // 31 : 31 32-bit variant Applies when sf == 0 && N == 0 && imms == 011111
757         // LSR <Wd>, <Wn>, #<shift> is equivalent to UBFM <Wd>, <Wn>, #<shift>, #31
758         // and is always the preferred disassembly
759         imms = 31; // 31 : 32-bit variant Applies
760     } else {
761         // 63 : 63 64-bit variant Applies when sf == 1 && N == 1 && imms == 111111
762         // LSR <Xd>, <Xn>, #<shift> is equivalent to UBFM <Xd>, <Xn>, #<shift>, #63
763         // and is always the preferred disassembly
764         imms = 63; // 63 : 64-bit variant Applies
765     }
766     Ubfm(rd, rn, shift, imms);
767 }
768 
Add(const Register & rd,const Register & rn,const Operand & operand)769 void AssemblerAarch64::Add(const Register &rd, const Register &rn, const Operand &operand)
770 {
771     if (operand.IsImmediate()) {
772         int64_t imm = static_cast<int64_t>(operand.ImmediateValue());
773         if (imm < 0) {
774             AddSubImm(SUB_Imm, rd, rn, false, -1 * imm);
775         } else {
776             AddSubImm(ADD_Imm, rd, rn, false, imm);
777         }
778     } else {
779         if (operand.IsShifted()) {
780             AddSubReg(ADD_Shift, rd, rn, false, operand);
781         } else {
782             AddSubReg(ADD_Extend, rd, rn, false, operand);
783         }
784     }
785 }
786 
Adds(const Register & rd,const Register & rn,const Operand & operand)787 void AssemblerAarch64::Adds(const Register &rd, const Register &rn, const Operand &operand)
788 {
789     if (operand.IsImmediate()) {
790         AddSubImm(ADD_Imm, rd, rn, true, operand.ImmediateValue());
791     } else {
792         if (operand.IsShifted()) {
793             AddSubReg(ADD_Shift, rd, rn, true, operand);
794         } else {
795             AddSubReg(ADD_Extend, rd, rn, true, operand);
796         }
797     }
798 }
799 
Sub(const Register & rd,const Register & rn,const Operand & operand)800 void AssemblerAarch64::Sub(const Register &rd, const Register &rn, const Operand &operand)
801 {
802     if (operand.IsImmediate()) {
803         int64_t imm = static_cast<int64_t>(operand.ImmediateValue());
804         if (imm < 0) {
805             AddSubImm(ADD_Imm, rd, rn, false, -1 * imm);
806         } else {
807             AddSubImm(SUB_Imm, rd, rn, false, imm);
808         }
809     } else {
810         if (operand.IsShifted()) {
811             AddSubReg(SUB_Shift, rd, rn, false, operand);
812         } else {
813             AddSubReg(SUB_Extend, rd, rn, false, operand);
814         }
815     }
816 }
817 
Subs(const Register & rd,const Register & rn,const Operand & operand)818 void AssemblerAarch64::Subs(const Register &rd, const Register &rn, const Operand &operand)
819 {
820     if (operand.IsImmediate()) {
821         AddSubImm(SUB_Imm, rd, rn, true, operand.ImmediateValue());
822     } else {
823         if (operand.IsShifted()) {
824             AddSubReg(SUB_Shift, rd, rn, true, operand);
825         } else {
826             AddSubReg(SUB_Extend, rd, rn, true, operand);
827         }
828     }
829 }
830 
IsAddSubImm(uint64_t imm)831 bool AssemblerAarch64::IsAddSubImm(uint64_t imm)
832 {
833     const uint64_t IMM12_MASK = (1 << ADD_SUB_Imm12_WIDTH) - 1;
834     if (imm <= IMM12_MASK) {
835         return true;
836     }
837 
838     if (((imm & IMM12_MASK) == 0) && ((imm & ~IMM12_MASK) <= IMM12_MASK)) {
839         return true;
840     }
841     return false;
842 }
843 
AddSubImm(AddSubOpCode op,const Register & rd,const Register & rn,bool setFlags,uint64_t imm)844 void AssemblerAarch64::AddSubImm(AddSubOpCode op, const Register &rd, const Register &rn, bool setFlags, uint64_t imm)
845 {
846     ASSERT(IsAddSubImm(imm));
847     uint32_t shift = 0;
848     const uint64_t IMM12_MASK = (1 << ADD_SUB_Imm12_WIDTH) - 1;
849     uint64_t imm12 = imm & (~IMM12_MASK);
850     if (imm12 != 0) {
851         shift = 1;
852     } else {
853         imm12 = imm;
854     }
855     uint32_t flags_field = ((setFlags ? 1 : 0) << ADD_SUB_S_LOWBITS) & ADD_SUB_S_MASK;
856     uint32_t imm_field = (imm12 << ADD_SUB_Imm12_LOWBITS) & ADD_SUB_Imm12_MASK;
857     uint32_t shift_field = (shift << ADD_SUB_Sh_LOWBITS) & ADD_SUB_Sh_MASK;
858     uint32_t code = Sf(!rd.IsW()) | op | flags_field | shift_field | imm_field | Rd(rd.GetId()) | Rn(rn.GetId());
859     EmitU32(code);
860 }
861 
AddSubReg(AddSubOpCode op,const Register & rd,const Register & rn,bool setFlags,const Operand & operand)862 void AssemblerAarch64::AddSubReg(AddSubOpCode op, const Register &rd, const Register &rn,
863                                  bool setFlags, const Operand &operand)
864 {
865     uint32_t flags_field = ((setFlags ? 1 : 0) << ADD_SUB_S_LOWBITS) & ADD_SUB_S_MASK;
866     uint32_t code = 0;
867     if (operand.IsShifted()) {
868         uint32_t shift_field = ((operand.GetShiftOption()) << ADD_SUB_Shift_LOWBITS) & ADD_SUB_Shift_MASK;
869         uint32_t shift_amount = ((operand.GetShiftAmount()) << ADD_SUB_ShiftAmount_LOWBITS) & ADD_SUB_ShiftAmount_MASK;
870         ASSERT((op == ADD_Shift) | (op == SUB_Shift));
871         code = Sf(!rd.IsW()) | op | flags_field | shift_field | Rm(operand.Reg().GetId()) |
872                   shift_amount | Rn(rn.GetId()) | Rd(rd.GetId());
873     } else {
874         ASSERT((op == ADD_Extend) | (op == SUB_Extend));
875         uint32_t extend_field =
876             (operand.GetExtendOption() << ADD_SUB_ExtendOption_LOWBITS) & ADD_SUB_ExtendOption_MASK;
877         uint32_t extend_shift = (operand.GetShiftAmount() << ADD_SUB_ExtendShift_LOWBITS) & ADD_SUB_ExtendShift_MASK;
878         code = Sf(!rd.IsW()) | op | flags_field | Rm(operand.Reg().GetId()) | extend_field |
879                   extend_shift | Rn(rn.GetId()) | Rd(rd.GetId());
880     }
881     EmitU32(code);
882 }
883 
Cmp(const Register & rd,const Operand & operand)884 void AssemblerAarch64::Cmp(const Register &rd, const Operand &operand)
885 {
886     Subs(Register(Zero, rd.GetType()), rd, operand);
887 }
888 
CMov(const Register & rd,const Register & rn,const Operand & operand,Condition cond)889 void AssemblerAarch64::CMov(const Register &rd, const Register &rn, const Operand &operand, Condition cond)
890 {
891     ASSERT(!operand.IsImmediate());
892     uint32_t cond_field = (cond << CSEL_Cond_LOWBITS) & CSEL_Cond_MASK;
893     uint32_t code = Sf(!rd.IsW()) | CSEL | Rm(operand.Reg().GetId()) | cond_field | Rn(rn.GetId()) | Rd(rd.GetId());
894     EmitU32(code);
895 }
896 
B(Label * label)897 void AssemblerAarch64::B(Label *label)
898 {
899     int32_t offsetImm = LinkAndGetInstOffsetToLabel(label);
900     // 2 : 2 means 4 bytes aligned.
901     offsetImm >>= 2;
902     B(offsetImm);
903 }
904 
B(int32_t imm)905 void AssemblerAarch64::B(int32_t imm)
906 {
907     uint32_t code = BranchOpCode::Branch | ((imm << BRANCH_Imm26_LOWBITS) & BRANCH_Imm26_MASK);
908     EmitU32(code);
909 }
910 
Br(const Register & rn)911 void AssemblerAarch64::Br(const Register &rn)
912 {
913     uint32_t code = BranchOpCode::BR | Rn(rn.GetId());
914     EmitU32(code);
915 }
916 
Bl(Label * label)917 void AssemblerAarch64::Bl(Label *label)
918 {
919     int32_t offsetImm = LinkAndGetInstOffsetToLabel(label);
920     // 2 : 2 means 4 bytes aligned.
921     offsetImm >>= 2;
922     Bl(offsetImm);
923 }
924 
Bl(int32_t imm)925 void AssemblerAarch64::Bl(int32_t imm)
926 {
927     uint32_t code = CallOpCode::BL | ((imm << BRANCH_Imm26_LOWBITS) & BRANCH_Imm26_MASK);
928     EmitU32(code);
929 }
930 
Blr(const Register & rn)931 void AssemblerAarch64::Blr(const Register &rn)
932 {
933     ASSERT(!rn.IsW());
934     uint32_t code = CallOpCode::BLR | Rn(rn.GetId());
935     EmitU32(code);
936 }
937 
B(Condition cond,Label * label)938 void AssemblerAarch64::B(Condition cond, Label *label)
939 {
940     int32_t offsetImm = LinkAndGetInstOffsetToLabel(label);
941     // 2 : 2 means 4 bytes aligned.
942     offsetImm >>= 2;
943     B(cond, offsetImm);
944 }
945 
B(Condition cond,int32_t imm)946 void AssemblerAarch64::B(Condition cond, int32_t imm)
947 {
948     uint32_t code = BranchOpCode::BranchCond | BranchImm19(imm) | cond;
949     EmitU32(code);
950 }
951 
Cbz(const Register & rt,Label * label)952 void AssemblerAarch64::Cbz(const Register &rt, Label *label)
953 {
954     int32_t offsetImm = LinkAndGetInstOffsetToLabel(label);
955     // 2 : 2 means 4 bytes aligned.
956     offsetImm >>= 2;
957     Cbz(rt, offsetImm);
958 }
959 
Cbnz(const Register & rt,Label * label)960 void AssemblerAarch64::Cbnz(const Register &rt, Label *label)
961 {
962     int32_t offsetImm = LinkAndGetInstOffsetToLabel(label);
963     // 2 : 2 means 4 bytes aligned.
964     offsetImm >>= 2;
965     Cbnz(rt, offsetImm);
966 }
967 
Cbz(const Register & rt,int32_t imm)968 void AssemblerAarch64::Cbz(const Register &rt, int32_t imm)
969 {
970     uint32_t code = Sf(!rt.IsW()) | BranchOpCode::CBZ | BranchImm19(imm) | rt.GetId();
971     EmitU32(code);
972 }
973 
Cbnz(const Register & rt,int32_t imm)974 void AssemblerAarch64::Cbnz(const Register &rt, int32_t imm)
975 {
976     uint32_t code = Sf(!rt.IsW()) | BranchOpCode::CBNZ | BranchImm19(imm) | rt.GetId();
977     EmitU32(code);
978 }
979 
Tbz(const Register & rt,int32_t bitPos,Label * label)980 void AssemblerAarch64::Tbz(const Register &rt, int32_t bitPos, Label *label)
981 {
982     int32_t offsetImm = LinkAndGetInstOffsetToLabel(label);
983     // 2 : 2 means 4 bytes aligned.
984     offsetImm >>= 2;
985     Tbz(rt, bitPos, offsetImm);
986 }
987 
Tbz(const Register & rt,int32_t bitPos,int32_t imm)988 void AssemblerAarch64::Tbz(const Register &rt, int32_t bitPos, int32_t imm)
989 {
990     uint32_t b5 = (bitPos << (BRANCH_B5_LOWBITS - 5)) & BRANCH_B5_MASK;
991     uint32_t b40 = (bitPos << BRANCH_B40_LOWBITS) & BRANCH_B40_MASK;
992     uint32_t imm14 = (imm << BRANCH_Imm14_LOWBITS) & BRANCH_Imm14_MASK;
993     uint32_t code = b5 | BranchOpCode::TBZ | b40 | imm14 | rt.GetId();
994     EmitU32(code);
995 }
996 
Tbnz(const Register & rt,int32_t bitPos,Label * label)997 void AssemblerAarch64::Tbnz(const Register &rt, int32_t bitPos, Label *label)
998 {
999     int32_t offsetImm = LinkAndGetInstOffsetToLabel(label);
1000     // 2 : 2 means 4 bytes aligned.
1001     offsetImm >>= 2;
1002     Tbnz(rt, bitPos, offsetImm);
1003 }
1004 
Tbnz(const Register & rt,int32_t bitPos,int32_t imm)1005 void AssemblerAarch64::Tbnz(const Register &rt, int32_t bitPos, int32_t imm)
1006 {
1007     uint32_t b5 = (bitPos << (BRANCH_B5_LOWBITS - 5)) & BRANCH_B5_MASK;
1008     uint32_t b40 = (bitPos << BRANCH_B40_LOWBITS) & BRANCH_B40_MASK;
1009     uint32_t imm14 = (imm <<BRANCH_Imm14_LOWBITS) & BRANCH_Imm14_MASK;
1010     uint32_t code = b5 | BranchOpCode::TBNZ | b40 | imm14 | rt.GetId();
1011     EmitU32(code);
1012 }
1013 
Tst(const Register & rn,const LogicalImmediate & imm)1014 void AssemblerAarch64::Tst(const Register &rn, const LogicalImmediate &imm)
1015 {
1016     Ands(Register(Zero, rn.GetType()), rn, imm);
1017 }
1018 
LinkAndGetInstOffsetToLabel(Label * label)1019 int32_t AssemblerAarch64::LinkAndGetInstOffsetToLabel(Label *label)
1020 {
1021     int32_t offset = 0;
1022     if (label->IsBound()) {
1023         offset = static_cast<int32_t>(label->GetPos() - GetCurrentPosition());
1024     } else {
1025         if (label->IsLinked()) {
1026             offset = static_cast<int32_t>(label->GetLinkedPos() - GetCurrentPosition());
1027         } else {
1028             offset = 0;
1029         }
1030         label->LinkTo(GetCurrentPosition());
1031     }
1032     return offset;
1033 }
1034 
Bind(Label * target)1035 void AssemblerAarch64::Bind(Label *target)
1036 {
1037     size_t pos = GetCurrentPosition();
1038     ASSERT(!target->IsBound());
1039         if (target->IsLinked()) {
1040         uint32_t linkPos = target->GetLinkedPos();
1041         while (linkPos != 0) {
1042             int32_t offset = GetLinkOffsetFromBranchInst(linkPos);
1043             int32_t disp = static_cast<int32_t>(pos - linkPos);
1044             SetRealOffsetToBranchInst(linkPos, disp);
1045             if (offset == 0) {
1046                 break;
1047             }
1048             linkPos = linkPos + offset;
1049         }
1050     }
1051     target->BindTo(pos);
1052 }
1053 
GetLinkOffsetFromBranchInst(int32_t pos)1054 int32_t AssemblerAarch64::GetLinkOffsetFromBranchInst(int32_t pos)
1055 {
1056     uint32_t branchCode = GetU32(pos);
1057     // 2 : 2 means 4 bytes aligned.
1058     int32_t immOffSet = ImmBranch(branchCode) << 2;
1059     return immOffSet;
1060 }
1061 
ImmBranch(uint32_t branchCode)1062 int32_t AssemblerAarch64::ImmBranch(uint32_t branchCode)
1063 {
1064     int32_t immOffset = 0;
1065     if ((branchCode & BranchFMask) == BranchOpCode::Branch) {
1066         immOffset = (branchCode & BRANCH_Imm26_MASK) >> BRANCH_Imm26_LOWBITS;
1067         if (immOffset & (1 << (BRANCH_Imm26_WIDTH - 1))) {
1068             // 31 : 31 means topmost bits of instruction "uint32_t"
1069             immOffset |= ((1 << (31 - BRANCH_Imm26_WIDTH)) - 1) << BRANCH_Imm26_WIDTH;
1070         }
1071     } else if ((branchCode & BranchCondFMask) == BranchOpCode::BranchCond) {
1072         immOffset = (branchCode & BRANCH_Imm19_MASK) >> BRANCH_Imm19_LOWBITS;
1073         if (immOffset & (1 << (BRANCH_Imm19_WIDTH - 1))) {
1074             // 31 : 31 means topmost bits of instruction "uint32_t"
1075             immOffset |= ((1 << (31 - BRANCH_Imm19_WIDTH)) - 1) << BRANCH_Imm19_WIDTH;
1076         }
1077     } else if ((branchCode & BranchCompareFMask) == BranchOpCode::CBZ) {
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 & BranchTestFMask) == BranchOpCode::TBZ) {
1084         immOffset = (branchCode & BRANCH_Imm14_MASK) >> BRANCH_Imm14_LOWBITS;
1085         if (immOffset & (1 << (BRANCH_Imm14_WIDTH - 1))) {
1086             // 31 : 31 means topmost bits of instruction "uint32_t"
1087             immOffset |= ((1 << (31 - BRANCH_Imm14_WIDTH)) - 1) << BRANCH_Imm14_WIDTH;
1088         }
1089     } else {
1090         UNREACHABLE();
1091     }
1092     return immOffset;
1093 }
1094 
SetRealOffsetToBranchInst(uint32_t linkPos,int32_t disp)1095 void AssemblerAarch64::SetRealOffsetToBranchInst(uint32_t linkPos, int32_t disp)
1096 {
1097     uint32_t branchCode = GetU32(linkPos);
1098     // 2 : 2 means 4 bytes aligned.
1099     uint32_t immOffset = disp >> 2;
1100 
1101     if ((branchCode & BranchFMask) == BranchOpCode::Branch) {
1102         branchCode &= ~BRANCH_Imm26_MASK;
1103         branchCode |= (immOffset << BRANCH_Imm26_LOWBITS) & BRANCH_Imm26_MASK;
1104     } else if ((branchCode & BranchCondFMask) == BranchOpCode::BranchCond) {
1105         branchCode &= ~BRANCH_Imm19_MASK;
1106         branchCode |= (immOffset << BRANCH_Imm19_LOWBITS) & BRANCH_Imm19_MASK;
1107     } else if ((branchCode & BranchCompareFMask) == BranchOpCode::CBZ) {
1108         branchCode &= ~BRANCH_Imm19_MASK;
1109         branchCode |= (immOffset << BRANCH_Imm19_LOWBITS) & BRANCH_Imm19_MASK;
1110     } else if ((branchCode & BranchTestFMask) == BranchOpCode::TBZ) {
1111         branchCode &= ~BRANCH_Imm14_MASK;
1112         branchCode |= (immOffset << BRANCH_Imm14_LOWBITS) & BRANCH_Imm14_MASK;
1113     }
1114     PutI32(linkPos, branchCode);
1115 }
1116 
Ret()1117 void AssemblerAarch64::Ret()
1118 {
1119     Ret(Register(X30));
1120 }
1121 
Ret(const Register & rn)1122 void AssemblerAarch64::Ret(const Register &rn)
1123 {
1124     uint32_t code = RetOpCode::Ret | Rn(rn.GetId());
1125     EmitU32(code);
1126 }
1127 
Brk(const Immediate & imm)1128 void AssemblerAarch64::Brk(const Immediate &imm)
1129 {
1130     uint32_t brk_number_field =
1131         (static_cast<uint32_t>(imm.Value()) << BRK_Imm16_LOWBITS) & BRK_Imm16_MASK;
1132     uint32_t code = BRKImm | brk_number_field;
1133     EmitU32(code);
1134 }
1135 
GetImmOfLdr(const MemoryOperand & operand,Scale scale,bool isRegX)1136 uint64_t AssemblerAarch64::GetImmOfLdr(const MemoryOperand &operand, Scale scale, bool isRegX)
1137 {
1138     ASSERT(operand.IsImmediateOffset());
1139     uint64_t imm = static_cast<uint64_t>(operand.GetImmediate().Value());
1140     if (operand.GetAddrMode() == OFFSET) {
1141         if (scale == Scale::H) {
1142             imm >>= 1;
1143         } else if (scale == Scale::Q) {
1144             if (isRegX) {
1145                 imm >>= 3;  // 3:  64 RegSise, imm/8 to remove trailing zeros
1146             } else {
1147                 imm >>= 2;  // 2: 32 RegSise, imm/4 to remove trailing zeros
1148             }
1149         }
1150     }
1151     return imm;
1152 }
1153 
GetOpcodeOfLdr(const MemoryOperand & operand,Scale scale)1154 uint64_t AssemblerAarch64::GetOpcodeOfLdr(const MemoryOperand &operand, Scale scale)
1155 {
1156     uint32_t op = 0;
1157     if (operand.IsImmediateOffset()) {
1158         switch (operand.GetAddrMode()) {
1159             case OFFSET: {
1160                 if (scale == Scale::B) {
1161                     op = LoadStoreOpCode::LDRB_Offset;
1162                 } else if (scale == Scale::H) {
1163                     op = LoadStoreOpCode::LDRH_Offset;
1164                 } else if (scale == Scale::Q) {
1165                     op = LoadStoreOpCode::LDR_Offset;
1166                 } else {
1167                     LOG_ECMA(FATAL) << "this branch is unreachable";
1168                     UNREACHABLE();
1169                 }
1170                 break;
1171             }
1172             case PREINDEX: {
1173                 if (scale == Scale::B) {
1174                     op = LoadStoreOpCode::LDRB_Pre;
1175                 } else if (scale == Scale::H) {
1176                     op = LoadStoreOpCode::LDRH_Pre;
1177                 } else if (scale == Scale::Q) {
1178                     op = LoadStoreOpCode::LDR_Pre;
1179                 } else {
1180                     LOG_ECMA(FATAL) << "this branch is unreachable";
1181                     UNREACHABLE();
1182                 }
1183                 break;
1184             }
1185             case POSTINDEX: {
1186                 if (scale == Scale::B) {
1187                     op = LoadStoreOpCode::LDRB_Post;
1188                 } else if (scale == Scale::H) {
1189                     op = LoadStoreOpCode::LDRH_Post;
1190                 } else if (scale == Scale::Q) {
1191                     op = LoadStoreOpCode::LDR_Post;
1192                 } else {
1193                     LOG_ECMA(FATAL) << "this branch is unreachable";
1194                     UNREACHABLE();
1195                 }
1196                 break;
1197             }
1198             default:
1199                 LOG_ECMA(FATAL) << "this branch is unreachable";
1200                 UNREACHABLE();
1201         }
1202     } else {
1203         if (scale == Scale::B) {
1204             op = LoadStoreOpCode::LDRB_Register;
1205         } else if (scale == Scale::H) {
1206             op = LoadStoreOpCode::LDRH_Register;
1207         } else if (scale == Scale::Q) {
1208             op = LoadStoreOpCode::LDR_Register;
1209         } else {
1210             LOG_ECMA(FATAL) << "this branch is unreachable";
1211             UNREACHABLE();
1212         }
1213     }
1214     return op;
1215 }
1216 
GetShiftOfLdr(const MemoryOperand & operand,Scale scale,bool isRegX)1217 uint32_t AssemblerAarch64::GetShiftOfLdr(const MemoryOperand &operand, Scale scale, bool isRegX)
1218 {
1219     uint32_t shift = 0;
1220     if (scale == Scale::B) {
1221         shift = operand.GetShiftOption() != Shift::NO_SHIFT;
1222     } else if (scale == Scale::H) {
1223         shift = operand.GetShiftAmount();
1224         ASSERT(shift == 0 || shift == 1);
1225         shift = (shift == 0) ? 0 : 1;
1226     } else if (scale == Scale::Q) {
1227         shift = operand.GetShiftAmount();
1228         if (isRegX) {
1229             // 3 : 3 means address aligned with 8bytes
1230             ASSERT(shift == 0 || shift == 3);
1231         } else {
1232             // 2 : 2 means address aligned with 4bytes
1233             ASSERT(shift == 0 || shift == 2);
1234         }
1235         shift = (shift == 0) ? 0 : 1;
1236     }
1237     return shift;
1238 }
1239 }   // namespace panda::ecmascript::aarch64
1240