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