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