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