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