1 // Copyright 2015, VIXL authors
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright notice,
8 // this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above copyright notice,
10 // this list of conditions and the following disclaimer in the documentation
11 // and/or other materials provided with the distribution.
12 // * Neither the name of ARM Limited nor the names of its contributors may be
13 // used to endorse or promote products derived from this software without
14 // specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 #include <cctype>
28
29 #include "macro-assembler-aarch64.h"
30
31 namespace vixl {
32 namespace aarch64 {
33
34
Release()35 void Pool::Release() {
36 if (--monitor_ == 0) {
37 // Ensure the pool has not been blocked for too long.
38 VIXL_ASSERT(masm_->GetCursorOffset() < checkpoint_);
39 }
40 }
41
42
SetNextCheckpoint(ptrdiff_t checkpoint)43 void Pool::SetNextCheckpoint(ptrdiff_t checkpoint) {
44 masm_->checkpoint_ = std::min(masm_->checkpoint_, checkpoint);
45 checkpoint_ = checkpoint;
46 }
47
48
LiteralPool(MacroAssembler * masm)49 LiteralPool::LiteralPool(MacroAssembler* masm)
50 : Pool(masm),
51 size_(0),
52 first_use_(-1),
53 recommended_checkpoint_(kNoCheckpointRequired) {}
54
55
~LiteralPool()56 LiteralPool::~LiteralPool() {
57 VIXL_ASSERT(IsEmpty());
58 VIXL_ASSERT(!IsBlocked());
59 for (std::vector<RawLiteral*>::iterator it = deleted_on_destruction_.begin();
60 it != deleted_on_destruction_.end();
61 it++) {
62 delete *it;
63 }
64 }
65
66
Reset()67 void LiteralPool::Reset() {
68 std::vector<RawLiteral*>::iterator it, end;
69 for (it = entries_.begin(), end = entries_.end(); it != end; ++it) {
70 RawLiteral* literal = *it;
71 if (literal->deletion_policy_ == RawLiteral::kDeletedOnPlacementByPool) {
72 delete literal;
73 }
74 }
75 entries_.clear();
76 size_ = 0;
77 first_use_ = -1;
78 Pool::Reset();
79 recommended_checkpoint_ = kNoCheckpointRequired;
80 }
81
82
CheckEmitFor(size_t amount,EmitOption option)83 void LiteralPool::CheckEmitFor(size_t amount, EmitOption option) {
84 if (IsEmpty() || IsBlocked()) return;
85
86 ptrdiff_t distance = masm_->GetCursorOffset() + amount - first_use_;
87 if (distance >= kRecommendedLiteralPoolRange) {
88 Emit(option);
89 }
90 }
91
92
93 // We use a subclass to access the protected `ExactAssemblyScope` constructor
94 // giving us control over the pools. This allows us to use this scope within
95 // code emitting pools without creating a circular dependency.
96 // We keep the constructor private to restrict usage of this helper class.
97 class ExactAssemblyScopeWithoutPoolsCheck : public ExactAssemblyScope {
98 private:
ExactAssemblyScopeWithoutPoolsCheck(MacroAssembler * masm,size_t size)99 ExactAssemblyScopeWithoutPoolsCheck(MacroAssembler* masm, size_t size)
100 : ExactAssemblyScope(masm,
101 size,
102 ExactAssemblyScope::kExactSize,
103 ExactAssemblyScope::kIgnorePools) {}
104
105 friend void LiteralPool::Emit(LiteralPool::EmitOption);
106 friend void VeneerPool::Emit(VeneerPool::EmitOption, size_t);
107 };
108
109
Emit(EmitOption option)110 void LiteralPool::Emit(EmitOption option) {
111 // There is an issue if we are asked to emit a blocked or empty pool.
112 VIXL_ASSERT(!IsBlocked());
113 VIXL_ASSERT(!IsEmpty());
114
115 size_t pool_size = GetSize();
116 size_t emit_size = pool_size;
117 if (option == kBranchRequired) emit_size += kInstructionSize;
118 Label end_of_pool;
119
120 VIXL_ASSERT(emit_size % kInstructionSize == 0);
121 {
122 CodeBufferCheckScope guard(masm_,
123 emit_size,
124 CodeBufferCheckScope::kCheck,
125 CodeBufferCheckScope::kExactSize);
126 #ifdef VIXL_DEBUG
127 // Also explicitly disallow usage of the `MacroAssembler` here.
128 masm_->SetAllowMacroInstructions(false);
129 #endif
130 if (option == kBranchRequired) {
131 ExactAssemblyScopeWithoutPoolsCheck guard(masm_, kInstructionSize);
132 masm_->b(&end_of_pool);
133 }
134
135 {
136 // Marker indicating the size of the literal pool in 32-bit words.
137 VIXL_ASSERT((pool_size % kWRegSizeInBytes) == 0);
138 ExactAssemblyScopeWithoutPoolsCheck guard(masm_, kInstructionSize);
139 masm_->ldr(xzr, static_cast<int>(pool_size / kWRegSizeInBytes));
140 }
141
142 // Now populate the literal pool.
143 std::vector<RawLiteral*>::iterator it, end;
144 for (it = entries_.begin(), end = entries_.end(); it != end; ++it) {
145 VIXL_ASSERT((*it)->IsUsed());
146 masm_->place(*it);
147 }
148
149 if (option == kBranchRequired) masm_->bind(&end_of_pool);
150 #ifdef VIXL_DEBUG
151 masm_->SetAllowMacroInstructions(true);
152 #endif
153 }
154
155 Reset();
156 }
157
158
AddEntry(RawLiteral * literal)159 void LiteralPool::AddEntry(RawLiteral* literal) {
160 // A literal must be registered immediately before its first use. Here we
161 // cannot control that it is its first use, but we check no code has been
162 // emitted since its last use.
163 VIXL_ASSERT(masm_->GetCursorOffset() == literal->GetLastUse());
164
165 UpdateFirstUse(masm_->GetCursorOffset());
166 VIXL_ASSERT(masm_->GetCursorOffset() >= first_use_);
167 entries_.push_back(literal);
168 size_ += literal->GetSize();
169 }
170
171
UpdateFirstUse(ptrdiff_t use_position)172 void LiteralPool::UpdateFirstUse(ptrdiff_t use_position) {
173 first_use_ = std::min(first_use_, use_position);
174 if (first_use_ == -1) {
175 first_use_ = use_position;
176 SetNextRecommendedCheckpoint(GetNextRecommendedCheckpoint());
177 SetNextCheckpoint(first_use_ + Instruction::kLoadLiteralRange);
178 } else {
179 VIXL_ASSERT(use_position > first_use_);
180 }
181 }
182
183
Reset()184 void VeneerPool::Reset() {
185 Pool::Reset();
186 unresolved_branches_.Reset();
187 }
188
189
Release()190 void VeneerPool::Release() {
191 if (--monitor_ == 0) {
192 VIXL_ASSERT(IsEmpty() ||
193 masm_->GetCursorOffset() <
194 unresolved_branches_.GetFirstLimit());
195 }
196 }
197
198
RegisterUnresolvedBranch(ptrdiff_t branch_pos,Label * label,ImmBranchType branch_type)199 void VeneerPool::RegisterUnresolvedBranch(ptrdiff_t branch_pos,
200 Label* label,
201 ImmBranchType branch_type) {
202 VIXL_ASSERT(!label->IsBound());
203 BranchInfo branch_info = BranchInfo(branch_pos, label, branch_type);
204 unresolved_branches_.insert(branch_info);
205 UpdateNextCheckPoint();
206 // TODO: In debug mode register the label with the assembler to make sure it
207 // is bound with masm Bind and not asm bind.
208 }
209
210
DeleteUnresolvedBranchInfoForLabel(Label * label)211 void VeneerPool::DeleteUnresolvedBranchInfoForLabel(Label* label) {
212 if (IsEmpty()) {
213 VIXL_ASSERT(checkpoint_ == kNoCheckpointRequired);
214 return;
215 }
216
217 if (label->IsLinked()) {
218 Label::LabelLinksIterator links_it(label);
219 for (; !links_it.Done(); links_it.Advance()) {
220 ptrdiff_t link_offset = *links_it.Current();
221 Instruction* link = masm_->GetInstructionAt(link_offset);
222
223 // ADR instructions are not handled.
224 if (BranchTypeUsesVeneers(link->GetBranchType())) {
225 BranchInfo branch_info(link_offset, label, link->GetBranchType());
226 unresolved_branches_.erase(branch_info);
227 }
228 }
229 }
230
231 UpdateNextCheckPoint();
232 }
233
234
ShouldEmitVeneer(int64_t max_reachable_pc,size_t amount)235 bool VeneerPool::ShouldEmitVeneer(int64_t max_reachable_pc, size_t amount) {
236 ptrdiff_t offset =
237 kPoolNonVeneerCodeSize + amount + GetMaxSize() + GetOtherPoolsMaxSize();
238 return (masm_->GetCursorOffset() + offset) > max_reachable_pc;
239 }
240
241
CheckEmitFor(size_t amount,EmitOption option)242 void VeneerPool::CheckEmitFor(size_t amount, EmitOption option) {
243 if (IsEmpty()) return;
244
245 VIXL_ASSERT(masm_->GetCursorOffset() < unresolved_branches_.GetFirstLimit());
246
247 if (IsBlocked()) return;
248
249 if (ShouldEmitVeneers(amount)) {
250 Emit(option, amount);
251 } else {
252 UpdateNextCheckPoint();
253 }
254 }
255
256
Emit(EmitOption option,size_t amount)257 void VeneerPool::Emit(EmitOption option, size_t amount) {
258 // There is an issue if we are asked to emit a blocked or empty pool.
259 VIXL_ASSERT(!IsBlocked());
260 VIXL_ASSERT(!IsEmpty());
261
262 Label end;
263 if (option == kBranchRequired) {
264 ExactAssemblyScopeWithoutPoolsCheck guard(masm_, kInstructionSize);
265 masm_->b(&end);
266 }
267
268 // We want to avoid generating veneer pools too often, so generate veneers for
269 // branches that don't immediately require a veneer but will soon go out of
270 // range.
271 static const size_t kVeneerEmissionMargin = 1 * KBytes;
272
273 for (BranchInfoSetIterator it(&unresolved_branches_); !it.Done();) {
274 BranchInfo* branch_info = it.Current();
275 if (ShouldEmitVeneer(branch_info->max_reachable_pc_,
276 amount + kVeneerEmissionMargin)) {
277 CodeBufferCheckScope scope(masm_,
278 kVeneerCodeSize,
279 CodeBufferCheckScope::kCheck,
280 CodeBufferCheckScope::kExactSize);
281 ptrdiff_t branch_pos = branch_info->pc_offset_;
282 Instruction* branch = masm_->GetInstructionAt(branch_pos);
283 Label* label = branch_info->label_;
284
285 // Patch the branch to point to the current position, and emit a branch
286 // to the label.
287 Instruction* veneer = masm_->GetCursorAddress<Instruction*>();
288 branch->SetImmPCOffsetTarget(veneer);
289 {
290 ExactAssemblyScopeWithoutPoolsCheck guard(masm_, kInstructionSize);
291 masm_->b(label);
292 }
293
294 // Update the label. The branch patched does not point to it any longer.
295 label->DeleteLink(branch_pos);
296
297 it.DeleteCurrentAndAdvance();
298 } else {
299 it.AdvanceToNextType();
300 }
301 }
302
303 UpdateNextCheckPoint();
304
305 masm_->bind(&end);
306 }
307
308
MacroAssembler(PositionIndependentCodeOption pic)309 MacroAssembler::MacroAssembler(PositionIndependentCodeOption pic)
310 : Assembler(pic),
311 #ifdef VIXL_DEBUG
312 allow_macro_instructions_(true),
313 #endif
314 generate_simulator_code_(VIXL_AARCH64_GENERATE_SIMULATOR_CODE),
315 sp_(sp),
316 tmp_list_(ip0, ip1),
317 fptmp_list_(d31),
318 current_scratch_scope_(NULL),
319 literal_pool_(this),
320 veneer_pool_(this),
321 recommended_checkpoint_(Pool::kNoCheckpointRequired) {
322 checkpoint_ = GetNextCheckPoint();
323 #ifndef VIXL_DEBUG
324 USE(allow_macro_instructions_);
325 #endif
326 }
327
328
MacroAssembler(size_t capacity,PositionIndependentCodeOption pic)329 MacroAssembler::MacroAssembler(size_t capacity,
330 PositionIndependentCodeOption pic)
331 : Assembler(capacity, pic),
332 #ifdef VIXL_DEBUG
333 allow_macro_instructions_(true),
334 #endif
335 generate_simulator_code_(VIXL_AARCH64_GENERATE_SIMULATOR_CODE),
336 sp_(sp),
337 tmp_list_(ip0, ip1),
338 fptmp_list_(d31),
339 current_scratch_scope_(NULL),
340 literal_pool_(this),
341 veneer_pool_(this),
342 recommended_checkpoint_(Pool::kNoCheckpointRequired) {
343 checkpoint_ = GetNextCheckPoint();
344 }
345
346
MacroAssembler(byte * buffer,size_t capacity,PositionIndependentCodeOption pic)347 MacroAssembler::MacroAssembler(byte* buffer,
348 size_t capacity,
349 PositionIndependentCodeOption pic)
350 : Assembler(buffer, capacity, pic),
351 #ifdef VIXL_DEBUG
352 allow_macro_instructions_(true),
353 #endif
354 generate_simulator_code_(VIXL_AARCH64_GENERATE_SIMULATOR_CODE),
355 sp_(sp),
356 tmp_list_(ip0, ip1),
357 fptmp_list_(d31),
358 current_scratch_scope_(NULL),
359 literal_pool_(this),
360 veneer_pool_(this),
361 recommended_checkpoint_(Pool::kNoCheckpointRequired) {
362 checkpoint_ = GetNextCheckPoint();
363 }
364
365
~MacroAssembler()366 MacroAssembler::~MacroAssembler() {}
367
368
Reset()369 void MacroAssembler::Reset() {
370 Assembler::Reset();
371
372 VIXL_ASSERT(!literal_pool_.IsBlocked());
373 literal_pool_.Reset();
374 veneer_pool_.Reset();
375
376 checkpoint_ = GetNextCheckPoint();
377 }
378
379
FinalizeCode()380 void MacroAssembler::FinalizeCode() {
381 if (!literal_pool_.IsEmpty()) literal_pool_.Emit();
382 VIXL_ASSERT(veneer_pool_.IsEmpty());
383
384 Assembler::FinalizeCode();
385 }
386
387
CheckEmitFor(size_t amount)388 void MacroAssembler::CheckEmitFor(size_t amount) {
389 CheckEmitPoolsFor(amount);
390 GetBuffer()->EnsureSpaceFor(amount);
391 }
392
393
CheckEmitPoolsFor(size_t amount)394 void MacroAssembler::CheckEmitPoolsFor(size_t amount) {
395 literal_pool_.CheckEmitFor(amount);
396 veneer_pool_.CheckEmitFor(amount);
397 checkpoint_ = GetNextCheckPoint();
398 }
399
400
MoveImmediateHelper(MacroAssembler * masm,const Register & rd,uint64_t imm)401 int MacroAssembler::MoveImmediateHelper(MacroAssembler* masm,
402 const Register& rd,
403 uint64_t imm) {
404 bool emit_code = (masm != NULL);
405 VIXL_ASSERT(IsUint32(imm) || IsInt32(imm) || rd.Is64Bits());
406 // The worst case for size is mov 64-bit immediate to sp:
407 // * up to 4 instructions to materialise the constant
408 // * 1 instruction to move to sp
409 MacroEmissionCheckScope guard(masm);
410
411 // Immediates on Aarch64 can be produced using an initial value, and zero to
412 // three move keep operations.
413 //
414 // Initial values can be generated with:
415 // 1. 64-bit move zero (movz).
416 // 2. 32-bit move inverted (movn).
417 // 3. 64-bit move inverted.
418 // 4. 32-bit orr immediate.
419 // 5. 64-bit orr immediate.
420 // Move-keep may then be used to modify each of the 16-bit half words.
421 //
422 // The code below supports all five initial value generators, and
423 // applying move-keep operations to move-zero and move-inverted initial
424 // values.
425
426 // Try to move the immediate in one instruction, and if that fails, switch to
427 // using multiple instructions.
428 if (OneInstrMoveImmediateHelper(masm, rd, imm)) {
429 return 1;
430 } else {
431 int instruction_count = 0;
432 unsigned reg_size = rd.GetSizeInBits();
433
434 // Generic immediate case. Imm will be represented by
435 // [imm3, imm2, imm1, imm0], where each imm is 16 bits.
436 // A move-zero or move-inverted is generated for the first non-zero or
437 // non-0xffff immX, and a move-keep for subsequent non-zero immX.
438
439 uint64_t ignored_halfword = 0;
440 bool invert_move = false;
441 // If the number of 0xffff halfwords is greater than the number of 0x0000
442 // halfwords, it's more efficient to use move-inverted.
443 if (CountClearHalfWords(~imm, reg_size) >
444 CountClearHalfWords(imm, reg_size)) {
445 ignored_halfword = 0xffff;
446 invert_move = true;
447 }
448
449 // Mov instructions can't move values into the stack pointer, so set up a
450 // temporary register, if needed.
451 UseScratchRegisterScope temps;
452 Register temp;
453 if (emit_code) {
454 temps.Open(masm);
455 temp = rd.IsSP() ? temps.AcquireSameSizeAs(rd) : rd;
456 }
457
458 // Iterate through the halfwords. Use movn/movz for the first non-ignored
459 // halfword, and movk for subsequent halfwords.
460 VIXL_ASSERT((reg_size % 16) == 0);
461 bool first_mov_done = false;
462 for (unsigned i = 0; i < (reg_size / 16); i++) {
463 uint64_t imm16 = (imm >> (16 * i)) & 0xffff;
464 if (imm16 != ignored_halfword) {
465 if (!first_mov_done) {
466 if (invert_move) {
467 if (emit_code) masm->movn(temp, ~imm16 & 0xffff, 16 * i);
468 instruction_count++;
469 } else {
470 if (emit_code) masm->movz(temp, imm16, 16 * i);
471 instruction_count++;
472 }
473 first_mov_done = true;
474 } else {
475 // Construct a wider constant.
476 if (emit_code) masm->movk(temp, imm16, 16 * i);
477 instruction_count++;
478 }
479 }
480 }
481
482 VIXL_ASSERT(first_mov_done);
483
484 // Move the temporary if the original destination register was the stack
485 // pointer.
486 if (rd.IsSP()) {
487 if (emit_code) masm->mov(rd, temp);
488 instruction_count++;
489 }
490 return instruction_count;
491 }
492 }
493
494
OneInstrMoveImmediateHelper(MacroAssembler * masm,const Register & dst,int64_t imm)495 bool MacroAssembler::OneInstrMoveImmediateHelper(MacroAssembler* masm,
496 const Register& dst,
497 int64_t imm) {
498 bool emit_code = masm != NULL;
499 unsigned n, imm_s, imm_r;
500 int reg_size = dst.GetSizeInBits();
501
502 if (IsImmMovz(imm, reg_size) && !dst.IsSP()) {
503 // Immediate can be represented in a move zero instruction. Movz can't write
504 // to the stack pointer.
505 if (emit_code) {
506 masm->movz(dst, imm);
507 }
508 return true;
509 } else if (IsImmMovn(imm, reg_size) && !dst.IsSP()) {
510 // Immediate can be represented in a move negative instruction. Movn can't
511 // write to the stack pointer.
512 if (emit_code) {
513 masm->movn(dst, dst.Is64Bits() ? ~imm : (~imm & kWRegMask));
514 }
515 return true;
516 } else if (IsImmLogical(imm, reg_size, &n, &imm_s, &imm_r)) {
517 // Immediate can be represented in a logical orr instruction.
518 VIXL_ASSERT(!dst.IsZero());
519 if (emit_code) {
520 masm->LogicalImmediate(dst,
521 AppropriateZeroRegFor(dst),
522 n,
523 imm_s,
524 imm_r,
525 ORR);
526 }
527 return true;
528 }
529 return false;
530 }
531
532
B(Label * label,BranchType type,Register reg,int bit)533 void MacroAssembler::B(Label* label, BranchType type, Register reg, int bit) {
534 VIXL_ASSERT((reg.Is(NoReg) || (type >= kBranchTypeFirstUsingReg)) &&
535 ((bit == -1) || (type >= kBranchTypeFirstUsingBit)));
536 if (kBranchTypeFirstCondition <= type && type <= kBranchTypeLastCondition) {
537 B(static_cast<Condition>(type), label);
538 } else {
539 switch (type) {
540 case always:
541 B(label);
542 break;
543 case never:
544 break;
545 case reg_zero:
546 Cbz(reg, label);
547 break;
548 case reg_not_zero:
549 Cbnz(reg, label);
550 break;
551 case reg_bit_clear:
552 Tbz(reg, bit, label);
553 break;
554 case reg_bit_set:
555 Tbnz(reg, bit, label);
556 break;
557 default:
558 VIXL_UNREACHABLE();
559 }
560 }
561 }
562
563
B(Label * label)564 void MacroAssembler::B(Label* label) {
565 SingleEmissionCheckScope guard(this);
566 b(label);
567 }
568
569
B(Label * label,Condition cond)570 void MacroAssembler::B(Label* label, Condition cond) {
571 VIXL_ASSERT(allow_macro_instructions_);
572 VIXL_ASSERT((cond != al) && (cond != nv));
573 EmissionCheckScope guard(this, 2 * kInstructionSize);
574
575 if (label->IsBound() && LabelIsOutOfRange(label, CondBranchType)) {
576 Label done;
577 b(&done, InvertCondition(cond));
578 b(label);
579 bind(&done);
580 } else {
581 if (!label->IsBound()) {
582 veneer_pool_.RegisterUnresolvedBranch(GetCursorOffset(),
583 label,
584 CondBranchType);
585 }
586 b(label, cond);
587 }
588 }
589
590
Cbnz(const Register & rt,Label * label)591 void MacroAssembler::Cbnz(const Register& rt, Label* label) {
592 VIXL_ASSERT(allow_macro_instructions_);
593 VIXL_ASSERT(!rt.IsZero());
594 EmissionCheckScope guard(this, 2 * kInstructionSize);
595
596 if (label->IsBound() && LabelIsOutOfRange(label, CondBranchType)) {
597 Label done;
598 cbz(rt, &done);
599 b(label);
600 bind(&done);
601 } else {
602 if (!label->IsBound()) {
603 veneer_pool_.RegisterUnresolvedBranch(GetCursorOffset(),
604 label,
605 CompareBranchType);
606 }
607 cbnz(rt, label);
608 }
609 }
610
611
Cbz(const Register & rt,Label * label)612 void MacroAssembler::Cbz(const Register& rt, Label* label) {
613 VIXL_ASSERT(allow_macro_instructions_);
614 VIXL_ASSERT(!rt.IsZero());
615 EmissionCheckScope guard(this, 2 * kInstructionSize);
616
617 if (label->IsBound() && LabelIsOutOfRange(label, CondBranchType)) {
618 Label done;
619 cbnz(rt, &done);
620 b(label);
621 bind(&done);
622 } else {
623 if (!label->IsBound()) {
624 veneer_pool_.RegisterUnresolvedBranch(GetCursorOffset(),
625 label,
626 CompareBranchType);
627 }
628 cbz(rt, label);
629 }
630 }
631
632
Tbnz(const Register & rt,unsigned bit_pos,Label * label)633 void MacroAssembler::Tbnz(const Register& rt, unsigned bit_pos, Label* label) {
634 VIXL_ASSERT(allow_macro_instructions_);
635 VIXL_ASSERT(!rt.IsZero());
636 EmissionCheckScope guard(this, 2 * kInstructionSize);
637
638 if (label->IsBound() && LabelIsOutOfRange(label, TestBranchType)) {
639 Label done;
640 tbz(rt, bit_pos, &done);
641 b(label);
642 bind(&done);
643 } else {
644 if (!label->IsBound()) {
645 veneer_pool_.RegisterUnresolvedBranch(GetCursorOffset(),
646 label,
647 TestBranchType);
648 }
649 tbnz(rt, bit_pos, label);
650 }
651 }
652
653
Tbz(const Register & rt,unsigned bit_pos,Label * label)654 void MacroAssembler::Tbz(const Register& rt, unsigned bit_pos, Label* label) {
655 VIXL_ASSERT(allow_macro_instructions_);
656 VIXL_ASSERT(!rt.IsZero());
657 EmissionCheckScope guard(this, 2 * kInstructionSize);
658
659 if (label->IsBound() && LabelIsOutOfRange(label, TestBranchType)) {
660 Label done;
661 tbnz(rt, bit_pos, &done);
662 b(label);
663 bind(&done);
664 } else {
665 if (!label->IsBound()) {
666 veneer_pool_.RegisterUnresolvedBranch(GetCursorOffset(),
667 label,
668 TestBranchType);
669 }
670 tbz(rt, bit_pos, label);
671 }
672 }
673
674
Bind(Label * label)675 void MacroAssembler::Bind(Label* label) {
676 VIXL_ASSERT(allow_macro_instructions_);
677 veneer_pool_.DeleteUnresolvedBranchInfoForLabel(label);
678 bind(label);
679 }
680
681
682 // Bind a label to a specified offset from the start of the buffer.
BindToOffset(Label * label,ptrdiff_t offset)683 void MacroAssembler::BindToOffset(Label* label, ptrdiff_t offset) {
684 VIXL_ASSERT(allow_macro_instructions_);
685 veneer_pool_.DeleteUnresolvedBranchInfoForLabel(label);
686 Assembler::BindToOffset(label, offset);
687 }
688
689
And(const Register & rd,const Register & rn,const Operand & operand)690 void MacroAssembler::And(const Register& rd,
691 const Register& rn,
692 const Operand& operand) {
693 VIXL_ASSERT(allow_macro_instructions_);
694 LogicalMacro(rd, rn, operand, AND);
695 }
696
697
Ands(const Register & rd,const Register & rn,const Operand & operand)698 void MacroAssembler::Ands(const Register& rd,
699 const Register& rn,
700 const Operand& operand) {
701 VIXL_ASSERT(allow_macro_instructions_);
702 LogicalMacro(rd, rn, operand, ANDS);
703 }
704
705
Tst(const Register & rn,const Operand & operand)706 void MacroAssembler::Tst(const Register& rn, const Operand& operand) {
707 VIXL_ASSERT(allow_macro_instructions_);
708 Ands(AppropriateZeroRegFor(rn), rn, operand);
709 }
710
711
Bic(const Register & rd,const Register & rn,const Operand & operand)712 void MacroAssembler::Bic(const Register& rd,
713 const Register& rn,
714 const Operand& operand) {
715 VIXL_ASSERT(allow_macro_instructions_);
716 LogicalMacro(rd, rn, operand, BIC);
717 }
718
719
Bics(const Register & rd,const Register & rn,const Operand & operand)720 void MacroAssembler::Bics(const Register& rd,
721 const Register& rn,
722 const Operand& operand) {
723 VIXL_ASSERT(allow_macro_instructions_);
724 LogicalMacro(rd, rn, operand, BICS);
725 }
726
727
Orr(const Register & rd,const Register & rn,const Operand & operand)728 void MacroAssembler::Orr(const Register& rd,
729 const Register& rn,
730 const Operand& operand) {
731 VIXL_ASSERT(allow_macro_instructions_);
732 LogicalMacro(rd, rn, operand, ORR);
733 }
734
735
Orn(const Register & rd,const Register & rn,const Operand & operand)736 void MacroAssembler::Orn(const Register& rd,
737 const Register& rn,
738 const Operand& operand) {
739 VIXL_ASSERT(allow_macro_instructions_);
740 LogicalMacro(rd, rn, operand, ORN);
741 }
742
743
Eor(const Register & rd,const Register & rn,const Operand & operand)744 void MacroAssembler::Eor(const Register& rd,
745 const Register& rn,
746 const Operand& operand) {
747 VIXL_ASSERT(allow_macro_instructions_);
748 LogicalMacro(rd, rn, operand, EOR);
749 }
750
751
Eon(const Register & rd,const Register & rn,const Operand & operand)752 void MacroAssembler::Eon(const Register& rd,
753 const Register& rn,
754 const Operand& operand) {
755 VIXL_ASSERT(allow_macro_instructions_);
756 LogicalMacro(rd, rn, operand, EON);
757 }
758
759
LogicalMacro(const Register & rd,const Register & rn,const Operand & operand,LogicalOp op)760 void MacroAssembler::LogicalMacro(const Register& rd,
761 const Register& rn,
762 const Operand& operand,
763 LogicalOp op) {
764 // The worst case for size is logical immediate to sp:
765 // * up to 4 instructions to materialise the constant
766 // * 1 instruction to do the operation
767 // * 1 instruction to move to sp
768 MacroEmissionCheckScope guard(this);
769 UseScratchRegisterScope temps(this);
770
771 if (operand.IsImmediate()) {
772 int64_t immediate = operand.GetImmediate();
773 unsigned reg_size = rd.GetSizeInBits();
774
775 // If the operation is NOT, invert the operation and immediate.
776 if ((op & NOT) == NOT) {
777 op = static_cast<LogicalOp>(op & ~NOT);
778 immediate = ~immediate;
779 }
780
781 // Ignore the top 32 bits of an immediate if we're moving to a W register.
782 if (rd.Is32Bits()) {
783 // Check that the top 32 bits are consistent.
784 VIXL_ASSERT(((immediate >> kWRegSize) == 0) ||
785 ((immediate >> kWRegSize) == -1));
786 immediate &= kWRegMask;
787 }
788
789 VIXL_ASSERT(rd.Is64Bits() || IsUint32(immediate));
790
791 // Special cases for all set or all clear immediates.
792 if (immediate == 0) {
793 switch (op) {
794 case AND:
795 Mov(rd, 0);
796 return;
797 case ORR:
798 VIXL_FALLTHROUGH();
799 case EOR:
800 Mov(rd, rn);
801 return;
802 case ANDS:
803 VIXL_FALLTHROUGH();
804 case BICS:
805 break;
806 default:
807 VIXL_UNREACHABLE();
808 }
809 } else if ((rd.Is64Bits() && (immediate == -1)) ||
810 (rd.Is32Bits() && (immediate == 0xffffffff))) {
811 switch (op) {
812 case AND:
813 Mov(rd, rn);
814 return;
815 case ORR:
816 Mov(rd, immediate);
817 return;
818 case EOR:
819 Mvn(rd, rn);
820 return;
821 case ANDS:
822 VIXL_FALLTHROUGH();
823 case BICS:
824 break;
825 default:
826 VIXL_UNREACHABLE();
827 }
828 }
829
830 unsigned n, imm_s, imm_r;
831 if (IsImmLogical(immediate, reg_size, &n, &imm_s, &imm_r)) {
832 // Immediate can be encoded in the instruction.
833 LogicalImmediate(rd, rn, n, imm_s, imm_r, op);
834 } else {
835 // Immediate can't be encoded: synthesize using move immediate.
836 Register temp = temps.AcquireSameSizeAs(rn);
837 Operand imm_operand = MoveImmediateForShiftedOp(temp, immediate);
838
839 if (rd.Is(sp)) {
840 // If rd is the stack pointer we cannot use it as the destination
841 // register so we use the temp register as an intermediate again.
842 Logical(temp, rn, imm_operand, op);
843 Mov(sp, temp);
844 } else {
845 Logical(rd, rn, imm_operand, op);
846 }
847 }
848 } else if (operand.IsExtendedRegister()) {
849 VIXL_ASSERT(operand.GetRegister().GetSizeInBits() <= rd.GetSizeInBits());
850 // Add/sub extended supports shift <= 4. We want to support exactly the
851 // same modes here.
852 VIXL_ASSERT(operand.GetShiftAmount() <= 4);
853 VIXL_ASSERT(
854 operand.GetRegister().Is64Bits() ||
855 ((operand.GetExtend() != UXTX) && (operand.GetExtend() != SXTX)));
856
857 temps.Exclude(operand.GetRegister());
858 Register temp = temps.AcquireSameSizeAs(rn);
859 EmitExtendShift(temp,
860 operand.GetRegister(),
861 operand.GetExtend(),
862 operand.GetShiftAmount());
863 Logical(rd, rn, Operand(temp), op);
864 } else {
865 // The operand can be encoded in the instruction.
866 VIXL_ASSERT(operand.IsShiftedRegister());
867 Logical(rd, rn, operand, op);
868 }
869 }
870
871
Mov(const Register & rd,const Operand & operand,DiscardMoveMode discard_mode)872 void MacroAssembler::Mov(const Register& rd,
873 const Operand& operand,
874 DiscardMoveMode discard_mode) {
875 VIXL_ASSERT(allow_macro_instructions_);
876 // The worst case for size is mov immediate with up to 4 instructions.
877 MacroEmissionCheckScope guard(this);
878
879 if (operand.IsImmediate()) {
880 // Call the macro assembler for generic immediates.
881 Mov(rd, operand.GetImmediate());
882 } else if (operand.IsShiftedRegister() && (operand.GetShiftAmount() != 0)) {
883 // Emit a shift instruction if moving a shifted register. This operation
884 // could also be achieved using an orr instruction (like orn used by Mvn),
885 // but using a shift instruction makes the disassembly clearer.
886 EmitShift(rd,
887 operand.GetRegister(),
888 operand.GetShift(),
889 operand.GetShiftAmount());
890 } else if (operand.IsExtendedRegister()) {
891 // Emit an extend instruction if moving an extended register. This handles
892 // extend with post-shift operations, too.
893 EmitExtendShift(rd,
894 operand.GetRegister(),
895 operand.GetExtend(),
896 operand.GetShiftAmount());
897 } else {
898 Mov(rd, operand.GetRegister(), discard_mode);
899 }
900 }
901
902
Movi16bitHelper(const VRegister & vd,uint64_t imm)903 void MacroAssembler::Movi16bitHelper(const VRegister& vd, uint64_t imm) {
904 VIXL_ASSERT(IsUint16(imm));
905 int byte1 = (imm & 0xff);
906 int byte2 = ((imm >> 8) & 0xff);
907 if (byte1 == byte2) {
908 movi(vd.Is64Bits() ? vd.V8B() : vd.V16B(), byte1);
909 } else if (byte1 == 0) {
910 movi(vd, byte2, LSL, 8);
911 } else if (byte2 == 0) {
912 movi(vd, byte1);
913 } else if (byte1 == 0xff) {
914 mvni(vd, ~byte2 & 0xff, LSL, 8);
915 } else if (byte2 == 0xff) {
916 mvni(vd, ~byte1 & 0xff);
917 } else {
918 UseScratchRegisterScope temps(this);
919 Register temp = temps.AcquireW();
920 movz(temp, imm);
921 dup(vd, temp);
922 }
923 }
924
925
Movi32bitHelper(const VRegister & vd,uint64_t imm)926 void MacroAssembler::Movi32bitHelper(const VRegister& vd, uint64_t imm) {
927 VIXL_ASSERT(IsUint32(imm));
928
929 uint8_t bytes[sizeof(imm)];
930 memcpy(bytes, &imm, sizeof(imm));
931
932 // All bytes are either 0x00 or 0xff.
933 {
934 bool all0orff = true;
935 for (int i = 0; i < 4; ++i) {
936 if ((bytes[i] != 0) && (bytes[i] != 0xff)) {
937 all0orff = false;
938 break;
939 }
940 }
941
942 if (all0orff == true) {
943 movi(vd.Is64Bits() ? vd.V1D() : vd.V2D(), ((imm << 32) | imm));
944 return;
945 }
946 }
947
948 // Of the 4 bytes, only one byte is non-zero.
949 for (int i = 0; i < 4; i++) {
950 if ((imm & (0xff << (i * 8))) == imm) {
951 movi(vd, bytes[i], LSL, i * 8);
952 return;
953 }
954 }
955
956 // Of the 4 bytes, only one byte is not 0xff.
957 for (int i = 0; i < 4; i++) {
958 uint32_t mask = ~(0xff << (i * 8));
959 if ((imm & mask) == mask) {
960 mvni(vd, ~bytes[i] & 0xff, LSL, i * 8);
961 return;
962 }
963 }
964
965 // Immediate is of the form 0x00MMFFFF.
966 if ((imm & 0xff00ffff) == 0x0000ffff) {
967 movi(vd, bytes[2], MSL, 16);
968 return;
969 }
970
971 // Immediate is of the form 0x0000MMFF.
972 if ((imm & 0xffff00ff) == 0x000000ff) {
973 movi(vd, bytes[1], MSL, 8);
974 return;
975 }
976
977 // Immediate is of the form 0xFFMM0000.
978 if ((imm & 0xff00ffff) == 0xff000000) {
979 mvni(vd, ~bytes[2] & 0xff, MSL, 16);
980 return;
981 }
982 // Immediate is of the form 0xFFFFMM00.
983 if ((imm & 0xffff00ff) == 0xffff0000) {
984 mvni(vd, ~bytes[1] & 0xff, MSL, 8);
985 return;
986 }
987
988 // Top and bottom 16-bits are equal.
989 if (((imm >> 16) & 0xffff) == (imm & 0xffff)) {
990 Movi16bitHelper(vd.Is64Bits() ? vd.V4H() : vd.V8H(), imm & 0xffff);
991 return;
992 }
993
994 // Default case.
995 {
996 UseScratchRegisterScope temps(this);
997 Register temp = temps.AcquireW();
998 Mov(temp, imm);
999 dup(vd, temp);
1000 }
1001 }
1002
1003
Movi64bitHelper(const VRegister & vd,uint64_t imm)1004 void MacroAssembler::Movi64bitHelper(const VRegister& vd, uint64_t imm) {
1005 // All bytes are either 0x00 or 0xff.
1006 {
1007 bool all0orff = true;
1008 for (int i = 0; i < 8; ++i) {
1009 int byteval = (imm >> (i * 8)) & 0xff;
1010 if (byteval != 0 && byteval != 0xff) {
1011 all0orff = false;
1012 break;
1013 }
1014 }
1015 if (all0orff == true) {
1016 movi(vd, imm);
1017 return;
1018 }
1019 }
1020
1021 // Top and bottom 32-bits are equal.
1022 if (((imm >> 32) & 0xffffffff) == (imm & 0xffffffff)) {
1023 Movi32bitHelper(vd.Is64Bits() ? vd.V2S() : vd.V4S(), imm & 0xffffffff);
1024 return;
1025 }
1026
1027 // Default case.
1028 {
1029 UseScratchRegisterScope temps(this);
1030 Register temp = temps.AcquireX();
1031 Mov(temp, imm);
1032 if (vd.Is1D()) {
1033 mov(vd.D(), 0, temp);
1034 } else {
1035 dup(vd.V2D(), temp);
1036 }
1037 }
1038 }
1039
1040
Movi(const VRegister & vd,uint64_t imm,Shift shift,int shift_amount)1041 void MacroAssembler::Movi(const VRegister& vd,
1042 uint64_t imm,
1043 Shift shift,
1044 int shift_amount) {
1045 VIXL_ASSERT(allow_macro_instructions_);
1046 MacroEmissionCheckScope guard(this);
1047 if (shift_amount != 0 || shift != LSL) {
1048 movi(vd, imm, shift, shift_amount);
1049 } else if (vd.Is8B() || vd.Is16B()) {
1050 // 8-bit immediate.
1051 VIXL_ASSERT(IsUint8(imm));
1052 movi(vd, imm);
1053 } else if (vd.Is4H() || vd.Is8H()) {
1054 // 16-bit immediate.
1055 Movi16bitHelper(vd, imm);
1056 } else if (vd.Is2S() || vd.Is4S()) {
1057 // 32-bit immediate.
1058 Movi32bitHelper(vd, imm);
1059 } else {
1060 // 64-bit immediate.
1061 Movi64bitHelper(vd, imm);
1062 }
1063 }
1064
1065
Movi(const VRegister & vd,uint64_t hi,uint64_t lo)1066 void MacroAssembler::Movi(const VRegister& vd, uint64_t hi, uint64_t lo) {
1067 // TODO: Move 128-bit values in a more efficient way.
1068 VIXL_ASSERT(vd.Is128Bits());
1069 UseScratchRegisterScope temps(this);
1070 Movi(vd.V2D(), lo);
1071 Register temp = temps.AcquireX();
1072 Mov(temp, hi);
1073 Ins(vd.V2D(), 1, temp);
1074 }
1075
1076
Mvn(const Register & rd,const Operand & operand)1077 void MacroAssembler::Mvn(const Register& rd, const Operand& operand) {
1078 VIXL_ASSERT(allow_macro_instructions_);
1079 // The worst case for size is mvn immediate with up to 4 instructions.
1080 MacroEmissionCheckScope guard(this);
1081
1082 if (operand.IsImmediate()) {
1083 // Call the macro assembler for generic immediates.
1084 Mvn(rd, operand.GetImmediate());
1085 } else if (operand.IsExtendedRegister()) {
1086 UseScratchRegisterScope temps(this);
1087 temps.Exclude(operand.GetRegister());
1088
1089 // Emit two instructions for the extend case. This differs from Mov, as
1090 // the extend and invert can't be achieved in one instruction.
1091 Register temp = temps.AcquireSameSizeAs(rd);
1092 EmitExtendShift(temp,
1093 operand.GetRegister(),
1094 operand.GetExtend(),
1095 operand.GetShiftAmount());
1096 mvn(rd, Operand(temp));
1097 } else {
1098 // Otherwise, register and shifted register cases can be handled by the
1099 // assembler directly, using orn.
1100 mvn(rd, operand);
1101 }
1102 }
1103
1104
Mov(const Register & rd,uint64_t imm)1105 void MacroAssembler::Mov(const Register& rd, uint64_t imm) {
1106 VIXL_ASSERT(allow_macro_instructions_);
1107 MoveImmediateHelper(this, rd, imm);
1108 }
1109
1110
Ccmp(const Register & rn,const Operand & operand,StatusFlags nzcv,Condition cond)1111 void MacroAssembler::Ccmp(const Register& rn,
1112 const Operand& operand,
1113 StatusFlags nzcv,
1114 Condition cond) {
1115 VIXL_ASSERT(allow_macro_instructions_);
1116 if (operand.IsImmediate() && (operand.GetImmediate() < 0)) {
1117 ConditionalCompareMacro(rn, -operand.GetImmediate(), nzcv, cond, CCMN);
1118 } else {
1119 ConditionalCompareMacro(rn, operand, nzcv, cond, CCMP);
1120 }
1121 }
1122
1123
Ccmn(const Register & rn,const Operand & operand,StatusFlags nzcv,Condition cond)1124 void MacroAssembler::Ccmn(const Register& rn,
1125 const Operand& operand,
1126 StatusFlags nzcv,
1127 Condition cond) {
1128 VIXL_ASSERT(allow_macro_instructions_);
1129 if (operand.IsImmediate() && (operand.GetImmediate() < 0)) {
1130 ConditionalCompareMacro(rn, -operand.GetImmediate(), nzcv, cond, CCMP);
1131 } else {
1132 ConditionalCompareMacro(rn, operand, nzcv, cond, CCMN);
1133 }
1134 }
1135
1136
ConditionalCompareMacro(const Register & rn,const Operand & operand,StatusFlags nzcv,Condition cond,ConditionalCompareOp op)1137 void MacroAssembler::ConditionalCompareMacro(const Register& rn,
1138 const Operand& operand,
1139 StatusFlags nzcv,
1140 Condition cond,
1141 ConditionalCompareOp op) {
1142 VIXL_ASSERT((cond != al) && (cond != nv));
1143 // The worst case for size is ccmp immediate:
1144 // * up to 4 instructions to materialise the constant
1145 // * 1 instruction for ccmp
1146 MacroEmissionCheckScope guard(this);
1147
1148 if ((operand.IsShiftedRegister() && (operand.GetShiftAmount() == 0)) ||
1149 (operand.IsImmediate() &&
1150 IsImmConditionalCompare(operand.GetImmediate()))) {
1151 // The immediate can be encoded in the instruction, or the operand is an
1152 // unshifted register: call the assembler.
1153 ConditionalCompare(rn, operand, nzcv, cond, op);
1154 } else {
1155 UseScratchRegisterScope temps(this);
1156 // The operand isn't directly supported by the instruction: perform the
1157 // operation on a temporary register.
1158 Register temp = temps.AcquireSameSizeAs(rn);
1159 Mov(temp, operand);
1160 ConditionalCompare(rn, temp, nzcv, cond, op);
1161 }
1162 }
1163
1164
CselHelper(MacroAssembler * masm,const Register & rd,Operand left,Operand right,Condition cond,bool * should_synthesise_left,bool * should_synthesise_right)1165 void MacroAssembler::CselHelper(MacroAssembler* masm,
1166 const Register& rd,
1167 Operand left,
1168 Operand right,
1169 Condition cond,
1170 bool* should_synthesise_left,
1171 bool* should_synthesise_right) {
1172 bool emit_code = (masm != NULL);
1173
1174 VIXL_ASSERT(!emit_code || masm->allow_macro_instructions_);
1175 VIXL_ASSERT((cond != al) && (cond != nv));
1176 VIXL_ASSERT(!rd.IsZero() && !rd.IsSP());
1177 VIXL_ASSERT(left.IsImmediate() || !left.GetRegister().IsSP());
1178 VIXL_ASSERT(right.IsImmediate() || !right.GetRegister().IsSP());
1179
1180 if (should_synthesise_left != NULL) *should_synthesise_left = false;
1181 if (should_synthesise_right != NULL) *should_synthesise_right = false;
1182
1183 // The worst case for size occurs when the inputs are two non encodable
1184 // constants:
1185 // * up to 4 instructions to materialise the left constant
1186 // * up to 4 instructions to materialise the right constant
1187 // * 1 instruction for csel
1188 EmissionCheckScope guard(masm, 9 * kInstructionSize);
1189 UseScratchRegisterScope temps;
1190 if (masm != NULL) {
1191 temps.Open(masm);
1192 }
1193
1194 // Try to handle cases where both inputs are immediates.
1195 bool left_is_immediate = left.IsImmediate() || left.IsZero();
1196 bool right_is_immediate = right.IsImmediate() || right.IsZero();
1197 if (left_is_immediate && right_is_immediate &&
1198 CselSubHelperTwoImmediates(masm,
1199 rd,
1200 left.GetEquivalentImmediate(),
1201 right.GetEquivalentImmediate(),
1202 cond,
1203 should_synthesise_left,
1204 should_synthesise_right)) {
1205 return;
1206 }
1207
1208 // Handle cases where one of the two inputs is -1, 0, or 1.
1209 bool left_is_small_immediate =
1210 left_is_immediate && ((-1 <= left.GetEquivalentImmediate()) &&
1211 (left.GetEquivalentImmediate() <= 1));
1212 bool right_is_small_immediate =
1213 right_is_immediate && ((-1 <= right.GetEquivalentImmediate()) &&
1214 (right.GetEquivalentImmediate() <= 1));
1215 if (right_is_small_immediate || left_is_small_immediate) {
1216 bool swapped_inputs = false;
1217 if (!right_is_small_immediate) {
1218 std::swap(left, right);
1219 cond = InvertCondition(cond);
1220 swapped_inputs = true;
1221 }
1222 CselSubHelperRightSmallImmediate(masm,
1223 &temps,
1224 rd,
1225 left,
1226 right,
1227 cond,
1228 swapped_inputs ? should_synthesise_right
1229 : should_synthesise_left);
1230 return;
1231 }
1232
1233 // Otherwise both inputs need to be available in registers. Synthesise them
1234 // if necessary and emit the `csel`.
1235 if (!left.IsPlainRegister()) {
1236 if (emit_code) {
1237 Register temp = temps.AcquireSameSizeAs(rd);
1238 masm->Mov(temp, left);
1239 left = temp;
1240 }
1241 if (should_synthesise_left != NULL) *should_synthesise_left = true;
1242 }
1243 if (!right.IsPlainRegister()) {
1244 if (emit_code) {
1245 Register temp = temps.AcquireSameSizeAs(rd);
1246 masm->Mov(temp, right);
1247 right = temp;
1248 }
1249 if (should_synthesise_right != NULL) *should_synthesise_right = true;
1250 }
1251 if (emit_code) {
1252 VIXL_ASSERT(left.IsPlainRegister() && right.IsPlainRegister());
1253 if (left.GetRegister().Is(right.GetRegister())) {
1254 masm->Mov(rd, left.GetRegister());
1255 } else {
1256 masm->csel(rd, left.GetRegister(), right.GetRegister(), cond);
1257 }
1258 }
1259 }
1260
1261
CselSubHelperTwoImmediates(MacroAssembler * masm,const Register & rd,int64_t left,int64_t right,Condition cond,bool * should_synthesise_left,bool * should_synthesise_right)1262 bool MacroAssembler::CselSubHelperTwoImmediates(MacroAssembler* masm,
1263 const Register& rd,
1264 int64_t left,
1265 int64_t right,
1266 Condition cond,
1267 bool* should_synthesise_left,
1268 bool* should_synthesise_right) {
1269 bool emit_code = (masm != NULL);
1270 if (should_synthesise_left != NULL) *should_synthesise_left = false;
1271 if (should_synthesise_right != NULL) *should_synthesise_right = false;
1272
1273 if (left == right) {
1274 if (emit_code) masm->Mov(rd, left);
1275 return true;
1276 } else if (left == -right) {
1277 if (should_synthesise_right != NULL) *should_synthesise_right = true;
1278 if (emit_code) {
1279 masm->Mov(rd, right);
1280 masm->Cneg(rd, rd, cond);
1281 }
1282 return true;
1283 }
1284
1285 if (CselSubHelperTwoOrderedImmediates(masm, rd, left, right, cond)) {
1286 return true;
1287 } else {
1288 std::swap(left, right);
1289 if (CselSubHelperTwoOrderedImmediates(masm,
1290 rd,
1291 left,
1292 right,
1293 InvertCondition(cond))) {
1294 return true;
1295 }
1296 }
1297
1298 // TODO: Handle more situations. For example handle `csel rd, #5, #6, cond`
1299 // with `cinc`.
1300 return false;
1301 }
1302
1303
CselSubHelperTwoOrderedImmediates(MacroAssembler * masm,const Register & rd,int64_t left,int64_t right,Condition cond)1304 bool MacroAssembler::CselSubHelperTwoOrderedImmediates(MacroAssembler* masm,
1305 const Register& rd,
1306 int64_t left,
1307 int64_t right,
1308 Condition cond) {
1309 bool emit_code = (masm != NULL);
1310
1311 if ((left == 1) && (right == 0)) {
1312 if (emit_code) masm->cset(rd, cond);
1313 return true;
1314 } else if ((left == -1) && (right == 0)) {
1315 if (emit_code) masm->csetm(rd, cond);
1316 return true;
1317 }
1318 return false;
1319 }
1320
1321
CselSubHelperRightSmallImmediate(MacroAssembler * masm,UseScratchRegisterScope * temps,const Register & rd,const Operand & left,const Operand & right,Condition cond,bool * should_synthesise_left)1322 void MacroAssembler::CselSubHelperRightSmallImmediate(
1323 MacroAssembler* masm,
1324 UseScratchRegisterScope* temps,
1325 const Register& rd,
1326 const Operand& left,
1327 const Operand& right,
1328 Condition cond,
1329 bool* should_synthesise_left) {
1330 bool emit_code = (masm != NULL);
1331 VIXL_ASSERT((right.IsImmediate() || right.IsZero()) &&
1332 (-1 <= right.GetEquivalentImmediate()) &&
1333 (right.GetEquivalentImmediate() <= 1));
1334 Register left_register;
1335
1336 if (left.IsPlainRegister()) {
1337 left_register = left.GetRegister();
1338 } else {
1339 if (emit_code) {
1340 left_register = temps->AcquireSameSizeAs(rd);
1341 masm->Mov(left_register, left);
1342 }
1343 if (should_synthesise_left != NULL) *should_synthesise_left = true;
1344 }
1345 if (emit_code) {
1346 int64_t imm = right.GetEquivalentImmediate();
1347 Register zr = AppropriateZeroRegFor(rd);
1348 if (imm == 0) {
1349 masm->csel(rd, left_register, zr, cond);
1350 } else if (imm == 1) {
1351 masm->csinc(rd, left_register, zr, cond);
1352 } else {
1353 VIXL_ASSERT(imm == -1);
1354 masm->csinv(rd, left_register, zr, cond);
1355 }
1356 }
1357 }
1358
1359
Add(const Register & rd,const Register & rn,const Operand & operand,FlagsUpdate S)1360 void MacroAssembler::Add(const Register& rd,
1361 const Register& rn,
1362 const Operand& operand,
1363 FlagsUpdate S) {
1364 VIXL_ASSERT(allow_macro_instructions_);
1365 if (operand.IsImmediate() && (operand.GetImmediate() < 0) &&
1366 IsImmAddSub(-operand.GetImmediate())) {
1367 AddSubMacro(rd, rn, -operand.GetImmediate(), S, SUB);
1368 } else {
1369 AddSubMacro(rd, rn, operand, S, ADD);
1370 }
1371 }
1372
1373
Adds(const Register & rd,const Register & rn,const Operand & operand)1374 void MacroAssembler::Adds(const Register& rd,
1375 const Register& rn,
1376 const Operand& operand) {
1377 Add(rd, rn, operand, SetFlags);
1378 }
1379
1380
Sub(const Register & rd,const Register & rn,const Operand & operand,FlagsUpdate S)1381 void MacroAssembler::Sub(const Register& rd,
1382 const Register& rn,
1383 const Operand& operand,
1384 FlagsUpdate S) {
1385 VIXL_ASSERT(allow_macro_instructions_);
1386 if (operand.IsImmediate() && (operand.GetImmediate() < 0) &&
1387 IsImmAddSub(-operand.GetImmediate())) {
1388 AddSubMacro(rd, rn, -operand.GetImmediate(), S, ADD);
1389 } else {
1390 AddSubMacro(rd, rn, operand, S, SUB);
1391 }
1392 }
1393
1394
Subs(const Register & rd,const Register & rn,const Operand & operand)1395 void MacroAssembler::Subs(const Register& rd,
1396 const Register& rn,
1397 const Operand& operand) {
1398 Sub(rd, rn, operand, SetFlags);
1399 }
1400
1401
Cmn(const Register & rn,const Operand & operand)1402 void MacroAssembler::Cmn(const Register& rn, const Operand& operand) {
1403 VIXL_ASSERT(allow_macro_instructions_);
1404 Adds(AppropriateZeroRegFor(rn), rn, operand);
1405 }
1406
1407
Cmp(const Register & rn,const Operand & operand)1408 void MacroAssembler::Cmp(const Register& rn, const Operand& operand) {
1409 VIXL_ASSERT(allow_macro_instructions_);
1410 Subs(AppropriateZeroRegFor(rn), rn, operand);
1411 }
1412
1413
Fcmp(const FPRegister & fn,double value,FPTrapFlags trap)1414 void MacroAssembler::Fcmp(const FPRegister& fn,
1415 double value,
1416 FPTrapFlags trap) {
1417 VIXL_ASSERT(allow_macro_instructions_);
1418 // The worst case for size is:
1419 // * 1 to materialise the constant, using literal pool if necessary
1420 // * 1 instruction for fcmp{e}
1421 MacroEmissionCheckScope guard(this);
1422 if (value != 0.0) {
1423 UseScratchRegisterScope temps(this);
1424 FPRegister tmp = temps.AcquireSameSizeAs(fn);
1425 Fmov(tmp, value);
1426 FPCompareMacro(fn, tmp, trap);
1427 } else {
1428 FPCompareMacro(fn, value, trap);
1429 }
1430 }
1431
1432
Fcmpe(const FPRegister & fn,double value)1433 void MacroAssembler::Fcmpe(const FPRegister& fn, double value) {
1434 Fcmp(fn, value, EnableTrap);
1435 }
1436
1437
Fmov(VRegister vd,double imm)1438 void MacroAssembler::Fmov(VRegister vd, double imm) {
1439 VIXL_ASSERT(allow_macro_instructions_);
1440 // Floating point immediates are loaded through the literal pool.
1441 MacroEmissionCheckScope guard(this);
1442
1443 if (vd.Is1S() || vd.Is2S() || vd.Is4S()) {
1444 Fmov(vd, static_cast<float>(imm));
1445 return;
1446 }
1447
1448 VIXL_ASSERT(vd.Is1D() || vd.Is2D());
1449 if (IsImmFP64(imm)) {
1450 fmov(vd, imm);
1451 } else {
1452 uint64_t rawbits = DoubleToRawbits(imm);
1453 if (vd.IsScalar()) {
1454 if (rawbits == 0) {
1455 fmov(vd, xzr);
1456 } else {
1457 ldr(vd,
1458 new Literal<double>(imm,
1459 &literal_pool_,
1460 RawLiteral::kDeletedOnPlacementByPool));
1461 }
1462 } else {
1463 // TODO: consider NEON support for load literal.
1464 Movi(vd, rawbits);
1465 }
1466 }
1467 }
1468
1469
Fmov(VRegister vd,float imm)1470 void MacroAssembler::Fmov(VRegister vd, float imm) {
1471 VIXL_ASSERT(allow_macro_instructions_);
1472 // Floating point immediates are loaded through the literal pool.
1473 MacroEmissionCheckScope guard(this);
1474
1475 if (vd.Is1D() || vd.Is2D()) {
1476 Fmov(vd, static_cast<double>(imm));
1477 return;
1478 }
1479
1480 VIXL_ASSERT(vd.Is1S() || vd.Is2S() || vd.Is4S());
1481 if (IsImmFP32(imm)) {
1482 fmov(vd, imm);
1483 } else {
1484 uint32_t rawbits = FloatToRawbits(imm);
1485 if (vd.IsScalar()) {
1486 if (rawbits == 0) {
1487 fmov(vd, wzr);
1488 } else {
1489 ldr(vd,
1490 new Literal<float>(imm,
1491 &literal_pool_,
1492 RawLiteral::kDeletedOnPlacementByPool));
1493 }
1494 } else {
1495 // TODO: consider NEON support for load literal.
1496 Movi(vd, rawbits);
1497 }
1498 }
1499 }
1500
1501
Neg(const Register & rd,const Operand & operand)1502 void MacroAssembler::Neg(const Register& rd, const Operand& operand) {
1503 VIXL_ASSERT(allow_macro_instructions_);
1504 if (operand.IsImmediate()) {
1505 Mov(rd, -operand.GetImmediate());
1506 } else {
1507 Sub(rd, AppropriateZeroRegFor(rd), operand);
1508 }
1509 }
1510
1511
Negs(const Register & rd,const Operand & operand)1512 void MacroAssembler::Negs(const Register& rd, const Operand& operand) {
1513 VIXL_ASSERT(allow_macro_instructions_);
1514 Subs(rd, AppropriateZeroRegFor(rd), operand);
1515 }
1516
1517
TryOneInstrMoveImmediate(const Register & dst,int64_t imm)1518 bool MacroAssembler::TryOneInstrMoveImmediate(const Register& dst,
1519 int64_t imm) {
1520 return OneInstrMoveImmediateHelper(this, dst, imm);
1521 }
1522
1523
MoveImmediateForShiftedOp(const Register & dst,int64_t imm)1524 Operand MacroAssembler::MoveImmediateForShiftedOp(const Register& dst,
1525 int64_t imm) {
1526 int reg_size = dst.GetSizeInBits();
1527
1528 // Encode the immediate in a single move instruction, if possible.
1529 if (TryOneInstrMoveImmediate(dst, imm)) {
1530 // The move was successful; nothing to do here.
1531 } else {
1532 // Pre-shift the immediate to the least-significant bits of the register.
1533 int shift_low = CountTrailingZeros(imm, reg_size);
1534 int64_t imm_low = imm >> shift_low;
1535
1536 // Pre-shift the immediate to the most-significant bits of the register,
1537 // inserting set bits in the least-significant bits.
1538 int shift_high = CountLeadingZeros(imm, reg_size);
1539 int64_t imm_high = (imm << shift_high) | ((INT64_C(1) << shift_high) - 1);
1540
1541 if (TryOneInstrMoveImmediate(dst, imm_low)) {
1542 // The new immediate has been moved into the destination's low bits:
1543 // return a new leftward-shifting operand.
1544 return Operand(dst, LSL, shift_low);
1545 } else if (TryOneInstrMoveImmediate(dst, imm_high)) {
1546 // The new immediate has been moved into the destination's high bits:
1547 // return a new rightward-shifting operand.
1548 return Operand(dst, LSR, shift_high);
1549 } else {
1550 Mov(dst, imm);
1551 }
1552 }
1553 return Operand(dst);
1554 }
1555
1556
Move(const GenericOperand & dst,const GenericOperand & src)1557 void MacroAssembler::Move(const GenericOperand& dst,
1558 const GenericOperand& src) {
1559 if (dst.Equals(src)) {
1560 return;
1561 }
1562
1563 VIXL_ASSERT(dst.IsValid() && src.IsValid());
1564
1565 // The sizes of the operands must match exactly.
1566 VIXL_ASSERT(dst.GetSizeInBits() == src.GetSizeInBits());
1567 VIXL_ASSERT(dst.GetSizeInBits() <= kXRegSize);
1568 int operand_size = static_cast<int>(dst.GetSizeInBits());
1569
1570 if (dst.IsCPURegister() && src.IsCPURegister()) {
1571 CPURegister dst_reg = dst.GetCPURegister();
1572 CPURegister src_reg = src.GetCPURegister();
1573 if (dst_reg.IsRegister() && src_reg.IsRegister()) {
1574 Mov(Register(dst_reg), Register(src_reg));
1575 } else if (dst_reg.IsVRegister() && src_reg.IsVRegister()) {
1576 Fmov(VRegister(dst_reg), VRegister(src_reg));
1577 } else {
1578 if (dst_reg.IsRegister()) {
1579 Fmov(Register(dst_reg), VRegister(src_reg));
1580 } else {
1581 Fmov(VRegister(dst_reg), Register(src_reg));
1582 }
1583 }
1584 return;
1585 }
1586
1587 if (dst.IsMemOperand() && src.IsMemOperand()) {
1588 UseScratchRegisterScope temps(this);
1589 CPURegister temp = temps.AcquireCPURegisterOfSize(operand_size);
1590 Ldr(temp, src.GetMemOperand());
1591 Str(temp, dst.GetMemOperand());
1592 return;
1593 }
1594
1595 if (dst.IsCPURegister()) {
1596 Ldr(dst.GetCPURegister(), src.GetMemOperand());
1597 } else {
1598 Str(src.GetCPURegister(), dst.GetMemOperand());
1599 }
1600 }
1601
1602
ComputeAddress(const Register & dst,const MemOperand & mem_op)1603 void MacroAssembler::ComputeAddress(const Register& dst,
1604 const MemOperand& mem_op) {
1605 // We cannot handle pre-indexing or post-indexing.
1606 VIXL_ASSERT(mem_op.GetAddrMode() == Offset);
1607 Register base = mem_op.GetBaseRegister();
1608 if (mem_op.IsImmediateOffset()) {
1609 Add(dst, base, mem_op.GetOffset());
1610 } else {
1611 VIXL_ASSERT(mem_op.IsRegisterOffset());
1612 Register reg_offset = mem_op.GetRegisterOffset();
1613 Shift shift = mem_op.GetShift();
1614 Extend extend = mem_op.GetExtend();
1615 if (shift == NO_SHIFT) {
1616 VIXL_ASSERT(extend != NO_EXTEND);
1617 Add(dst, base, Operand(reg_offset, extend, mem_op.GetShiftAmount()));
1618 } else {
1619 VIXL_ASSERT(extend == NO_EXTEND);
1620 Add(dst, base, Operand(reg_offset, shift, mem_op.GetShiftAmount()));
1621 }
1622 }
1623 }
1624
1625
AddSubMacro(const Register & rd,const Register & rn,const Operand & operand,FlagsUpdate S,AddSubOp op)1626 void MacroAssembler::AddSubMacro(const Register& rd,
1627 const Register& rn,
1628 const Operand& operand,
1629 FlagsUpdate S,
1630 AddSubOp op) {
1631 // Worst case is add/sub immediate:
1632 // * up to 4 instructions to materialise the constant
1633 // * 1 instruction for add/sub
1634 MacroEmissionCheckScope guard(this);
1635
1636 if (operand.IsZero() && rd.Is(rn) && rd.Is64Bits() && rn.Is64Bits() &&
1637 (S == LeaveFlags)) {
1638 // The instruction would be a nop. Avoid generating useless code.
1639 return;
1640 }
1641
1642 if ((operand.IsImmediate() && !IsImmAddSub(operand.GetImmediate())) ||
1643 (rn.IsZero() && !operand.IsShiftedRegister()) ||
1644 (operand.IsShiftedRegister() && (operand.GetShift() == ROR))) {
1645 UseScratchRegisterScope temps(this);
1646 Register temp = temps.AcquireSameSizeAs(rn);
1647 if (operand.IsImmediate()) {
1648 Operand imm_operand =
1649 MoveImmediateForShiftedOp(temp, operand.GetImmediate());
1650 AddSub(rd, rn, imm_operand, S, op);
1651 } else {
1652 Mov(temp, operand);
1653 AddSub(rd, rn, temp, S, op);
1654 }
1655 } else {
1656 AddSub(rd, rn, operand, S, op);
1657 }
1658 }
1659
1660
Adc(const Register & rd,const Register & rn,const Operand & operand)1661 void MacroAssembler::Adc(const Register& rd,
1662 const Register& rn,
1663 const Operand& operand) {
1664 VIXL_ASSERT(allow_macro_instructions_);
1665 AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, ADC);
1666 }
1667
1668
Adcs(const Register & rd,const Register & rn,const Operand & operand)1669 void MacroAssembler::Adcs(const Register& rd,
1670 const Register& rn,
1671 const Operand& operand) {
1672 VIXL_ASSERT(allow_macro_instructions_);
1673 AddSubWithCarryMacro(rd, rn, operand, SetFlags, ADC);
1674 }
1675
1676
Sbc(const Register & rd,const Register & rn,const Operand & operand)1677 void MacroAssembler::Sbc(const Register& rd,
1678 const Register& rn,
1679 const Operand& operand) {
1680 VIXL_ASSERT(allow_macro_instructions_);
1681 AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, SBC);
1682 }
1683
1684
Sbcs(const Register & rd,const Register & rn,const Operand & operand)1685 void MacroAssembler::Sbcs(const Register& rd,
1686 const Register& rn,
1687 const Operand& operand) {
1688 VIXL_ASSERT(allow_macro_instructions_);
1689 AddSubWithCarryMacro(rd, rn, operand, SetFlags, SBC);
1690 }
1691
1692
Ngc(const Register & rd,const Operand & operand)1693 void MacroAssembler::Ngc(const Register& rd, const Operand& operand) {
1694 VIXL_ASSERT(allow_macro_instructions_);
1695 Register zr = AppropriateZeroRegFor(rd);
1696 Sbc(rd, zr, operand);
1697 }
1698
1699
Ngcs(const Register & rd,const Operand & operand)1700 void MacroAssembler::Ngcs(const Register& rd, const Operand& operand) {
1701 VIXL_ASSERT(allow_macro_instructions_);
1702 Register zr = AppropriateZeroRegFor(rd);
1703 Sbcs(rd, zr, operand);
1704 }
1705
1706
AddSubWithCarryMacro(const Register & rd,const Register & rn,const Operand & operand,FlagsUpdate S,AddSubWithCarryOp op)1707 void MacroAssembler::AddSubWithCarryMacro(const Register& rd,
1708 const Register& rn,
1709 const Operand& operand,
1710 FlagsUpdate S,
1711 AddSubWithCarryOp op) {
1712 VIXL_ASSERT(rd.GetSizeInBits() == rn.GetSizeInBits());
1713 // Worst case is addc/subc immediate:
1714 // * up to 4 instructions to materialise the constant
1715 // * 1 instruction for add/sub
1716 MacroEmissionCheckScope guard(this);
1717 UseScratchRegisterScope temps(this);
1718
1719 if (operand.IsImmediate() ||
1720 (operand.IsShiftedRegister() && (operand.GetShift() == ROR))) {
1721 // Add/sub with carry (immediate or ROR shifted register.)
1722 Register temp = temps.AcquireSameSizeAs(rn);
1723 Mov(temp, operand);
1724 AddSubWithCarry(rd, rn, Operand(temp), S, op);
1725 } else if (operand.IsShiftedRegister() && (operand.GetShiftAmount() != 0)) {
1726 // Add/sub with carry (shifted register).
1727 VIXL_ASSERT(operand.GetRegister().GetSizeInBits() == rd.GetSizeInBits());
1728 VIXL_ASSERT(operand.GetShift() != ROR);
1729 VIXL_ASSERT(
1730 IsUintN(rd.GetSizeInBits() == kXRegSize ? kXRegSizeLog2 : kWRegSizeLog2,
1731 operand.GetShiftAmount()));
1732 temps.Exclude(operand.GetRegister());
1733 Register temp = temps.AcquireSameSizeAs(rn);
1734 EmitShift(temp,
1735 operand.GetRegister(),
1736 operand.GetShift(),
1737 operand.GetShiftAmount());
1738 AddSubWithCarry(rd, rn, Operand(temp), S, op);
1739 } else if (operand.IsExtendedRegister()) {
1740 // Add/sub with carry (extended register).
1741 VIXL_ASSERT(operand.GetRegister().GetSizeInBits() <= rd.GetSizeInBits());
1742 // Add/sub extended supports a shift <= 4. We want to support exactly the
1743 // same modes.
1744 VIXL_ASSERT(operand.GetShiftAmount() <= 4);
1745 VIXL_ASSERT(
1746 operand.GetRegister().Is64Bits() ||
1747 ((operand.GetExtend() != UXTX) && (operand.GetExtend() != SXTX)));
1748 temps.Exclude(operand.GetRegister());
1749 Register temp = temps.AcquireSameSizeAs(rn);
1750 EmitExtendShift(temp,
1751 operand.GetRegister(),
1752 operand.GetExtend(),
1753 operand.GetShiftAmount());
1754 AddSubWithCarry(rd, rn, Operand(temp), S, op);
1755 } else {
1756 // The addressing mode is directly supported by the instruction.
1757 AddSubWithCarry(rd, rn, operand, S, op);
1758 }
1759 }
1760
1761
1762 #define DEFINE_FUNCTION(FN, REGTYPE, REG, OP) \
1763 void MacroAssembler::FN(const REGTYPE REG, const MemOperand& addr) { \
1764 VIXL_ASSERT(allow_macro_instructions_); \
1765 LoadStoreMacro(REG, addr, OP); \
1766 }
LS_MACRO_LIST(DEFINE_FUNCTION)1767 LS_MACRO_LIST(DEFINE_FUNCTION)
1768 #undef DEFINE_FUNCTION
1769
1770
1771 void MacroAssembler::LoadStoreMacro(const CPURegister& rt,
1772 const MemOperand& addr,
1773 LoadStoreOp op) {
1774 // Worst case is ldr/str pre/post index:
1775 // * 1 instruction for ldr/str
1776 // * up to 4 instructions to materialise the constant
1777 // * 1 instruction to update the base
1778 MacroEmissionCheckScope guard(this);
1779
1780 int64_t offset = addr.GetOffset();
1781 unsigned access_size = CalcLSDataSize(op);
1782
1783 // Check if an immediate offset fits in the immediate field of the
1784 // appropriate instruction. If not, emit two instructions to perform
1785 // the operation.
1786 if (addr.IsImmediateOffset() && !IsImmLSScaled(offset, access_size) &&
1787 !IsImmLSUnscaled(offset)) {
1788 // Immediate offset that can't be encoded using unsigned or unscaled
1789 // addressing modes.
1790 UseScratchRegisterScope temps(this);
1791 Register temp = temps.AcquireSameSizeAs(addr.GetBaseRegister());
1792 Mov(temp, addr.GetOffset());
1793 LoadStore(rt, MemOperand(addr.GetBaseRegister(), temp), op);
1794 } else if (addr.IsPostIndex() && !IsImmLSUnscaled(offset)) {
1795 // Post-index beyond unscaled addressing range.
1796 LoadStore(rt, MemOperand(addr.GetBaseRegister()), op);
1797 Add(addr.GetBaseRegister(), addr.GetBaseRegister(), Operand(offset));
1798 } else if (addr.IsPreIndex() && !IsImmLSUnscaled(offset)) {
1799 // Pre-index beyond unscaled addressing range.
1800 Add(addr.GetBaseRegister(), addr.GetBaseRegister(), Operand(offset));
1801 LoadStore(rt, MemOperand(addr.GetBaseRegister()), op);
1802 } else {
1803 // Encodable in one load/store instruction.
1804 LoadStore(rt, addr, op);
1805 }
1806 }
1807
1808
1809 #define DEFINE_FUNCTION(FN, REGTYPE, REG, REG2, OP) \
1810 void MacroAssembler::FN(const REGTYPE REG, \
1811 const REGTYPE REG2, \
1812 const MemOperand& addr) { \
1813 VIXL_ASSERT(allow_macro_instructions_); \
1814 LoadStorePairMacro(REG, REG2, addr, OP); \
1815 }
LSPAIR_MACRO_LIST(DEFINE_FUNCTION)1816 LSPAIR_MACRO_LIST(DEFINE_FUNCTION)
1817 #undef DEFINE_FUNCTION
1818
1819 void MacroAssembler::LoadStorePairMacro(const CPURegister& rt,
1820 const CPURegister& rt2,
1821 const MemOperand& addr,
1822 LoadStorePairOp op) {
1823 // TODO(all): Should we support register offset for load-store-pair?
1824 VIXL_ASSERT(!addr.IsRegisterOffset());
1825 // Worst case is ldp/stp immediate:
1826 // * 1 instruction for ldp/stp
1827 // * up to 4 instructions to materialise the constant
1828 // * 1 instruction to update the base
1829 MacroEmissionCheckScope guard(this);
1830
1831 int64_t offset = addr.GetOffset();
1832 unsigned access_size = CalcLSPairDataSize(op);
1833
1834 // Check if the offset fits in the immediate field of the appropriate
1835 // instruction. If not, emit two instructions to perform the operation.
1836 if (IsImmLSPair(offset, access_size)) {
1837 // Encodable in one load/store pair instruction.
1838 LoadStorePair(rt, rt2, addr, op);
1839 } else {
1840 Register base = addr.GetBaseRegister();
1841 if (addr.IsImmediateOffset()) {
1842 UseScratchRegisterScope temps(this);
1843 Register temp = temps.AcquireSameSizeAs(base);
1844 Add(temp, base, offset);
1845 LoadStorePair(rt, rt2, MemOperand(temp), op);
1846 } else if (addr.IsPostIndex()) {
1847 LoadStorePair(rt, rt2, MemOperand(base), op);
1848 Add(base, base, offset);
1849 } else {
1850 VIXL_ASSERT(addr.IsPreIndex());
1851 Add(base, base, offset);
1852 LoadStorePair(rt, rt2, MemOperand(base), op);
1853 }
1854 }
1855 }
1856
1857
Prfm(PrefetchOperation op,const MemOperand & addr)1858 void MacroAssembler::Prfm(PrefetchOperation op, const MemOperand& addr) {
1859 MacroEmissionCheckScope guard(this);
1860
1861 // There are no pre- or post-index modes for prfm.
1862 VIXL_ASSERT(addr.IsImmediateOffset() || addr.IsRegisterOffset());
1863
1864 // The access size is implicitly 8 bytes for all prefetch operations.
1865 unsigned size = kXRegSizeInBytesLog2;
1866
1867 // Check if an immediate offset fits in the immediate field of the
1868 // appropriate instruction. If not, emit two instructions to perform
1869 // the operation.
1870 if (addr.IsImmediateOffset() && !IsImmLSScaled(addr.GetOffset(), size) &&
1871 !IsImmLSUnscaled(addr.GetOffset())) {
1872 // Immediate offset that can't be encoded using unsigned or unscaled
1873 // addressing modes.
1874 UseScratchRegisterScope temps(this);
1875 Register temp = temps.AcquireSameSizeAs(addr.GetBaseRegister());
1876 Mov(temp, addr.GetOffset());
1877 Prefetch(op, MemOperand(addr.GetBaseRegister(), temp));
1878 } else {
1879 // Simple register-offsets are encodable in one instruction.
1880 Prefetch(op, addr);
1881 }
1882 }
1883
1884
Push(const CPURegister & src0,const CPURegister & src1,const CPURegister & src2,const CPURegister & src3)1885 void MacroAssembler::Push(const CPURegister& src0,
1886 const CPURegister& src1,
1887 const CPURegister& src2,
1888 const CPURegister& src3) {
1889 VIXL_ASSERT(allow_macro_instructions_);
1890 VIXL_ASSERT(AreSameSizeAndType(src0, src1, src2, src3));
1891 VIXL_ASSERT(src0.IsValid());
1892
1893 int count = 1 + src1.IsValid() + src2.IsValid() + src3.IsValid();
1894 int size = src0.GetSizeInBytes();
1895
1896 PrepareForPush(count, size);
1897 PushHelper(count, size, src0, src1, src2, src3);
1898 }
1899
1900
Pop(const CPURegister & dst0,const CPURegister & dst1,const CPURegister & dst2,const CPURegister & dst3)1901 void MacroAssembler::Pop(const CPURegister& dst0,
1902 const CPURegister& dst1,
1903 const CPURegister& dst2,
1904 const CPURegister& dst3) {
1905 // It is not valid to pop into the same register more than once in one
1906 // instruction, not even into the zero register.
1907 VIXL_ASSERT(allow_macro_instructions_);
1908 VIXL_ASSERT(!AreAliased(dst0, dst1, dst2, dst3));
1909 VIXL_ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3));
1910 VIXL_ASSERT(dst0.IsValid());
1911
1912 int count = 1 + dst1.IsValid() + dst2.IsValid() + dst3.IsValid();
1913 int size = dst0.GetSizeInBytes();
1914
1915 PrepareForPop(count, size);
1916 PopHelper(count, size, dst0, dst1, dst2, dst3);
1917 }
1918
1919
PushCPURegList(CPURegList registers)1920 void MacroAssembler::PushCPURegList(CPURegList registers) {
1921 VIXL_ASSERT(!registers.Overlaps(*GetScratchRegisterList()));
1922 VIXL_ASSERT(!registers.Overlaps(*GetScratchFPRegisterList()));
1923 VIXL_ASSERT(allow_macro_instructions_);
1924
1925 int reg_size = registers.GetRegisterSizeInBytes();
1926 PrepareForPush(registers.GetCount(), reg_size);
1927
1928 // Bump the stack pointer and store two registers at the bottom.
1929 int size = registers.GetTotalSizeInBytes();
1930 const CPURegister& bottom_0 = registers.PopLowestIndex();
1931 const CPURegister& bottom_1 = registers.PopLowestIndex();
1932 if (bottom_0.IsValid() && bottom_1.IsValid()) {
1933 Stp(bottom_0, bottom_1, MemOperand(StackPointer(), -size, PreIndex));
1934 } else if (bottom_0.IsValid()) {
1935 Str(bottom_0, MemOperand(StackPointer(), -size, PreIndex));
1936 }
1937
1938 int offset = 2 * reg_size;
1939 while (!registers.IsEmpty()) {
1940 const CPURegister& src0 = registers.PopLowestIndex();
1941 const CPURegister& src1 = registers.PopLowestIndex();
1942 if (src1.IsValid()) {
1943 Stp(src0, src1, MemOperand(StackPointer(), offset));
1944 } else {
1945 Str(src0, MemOperand(StackPointer(), offset));
1946 }
1947 offset += 2 * reg_size;
1948 }
1949 }
1950
1951
PopCPURegList(CPURegList registers)1952 void MacroAssembler::PopCPURegList(CPURegList registers) {
1953 VIXL_ASSERT(!registers.Overlaps(*GetScratchRegisterList()));
1954 VIXL_ASSERT(!registers.Overlaps(*GetScratchFPRegisterList()));
1955 VIXL_ASSERT(allow_macro_instructions_);
1956
1957 int reg_size = registers.GetRegisterSizeInBytes();
1958 PrepareForPop(registers.GetCount(), reg_size);
1959
1960
1961 int size = registers.GetTotalSizeInBytes();
1962 const CPURegister& bottom_0 = registers.PopLowestIndex();
1963 const CPURegister& bottom_1 = registers.PopLowestIndex();
1964
1965 int offset = 2 * reg_size;
1966 while (!registers.IsEmpty()) {
1967 const CPURegister& dst0 = registers.PopLowestIndex();
1968 const CPURegister& dst1 = registers.PopLowestIndex();
1969 if (dst1.IsValid()) {
1970 Ldp(dst0, dst1, MemOperand(StackPointer(), offset));
1971 } else {
1972 Ldr(dst0, MemOperand(StackPointer(), offset));
1973 }
1974 offset += 2 * reg_size;
1975 }
1976
1977 // Load the two registers at the bottom and drop the stack pointer.
1978 if (bottom_0.IsValid() && bottom_1.IsValid()) {
1979 Ldp(bottom_0, bottom_1, MemOperand(StackPointer(), size, PostIndex));
1980 } else if (bottom_0.IsValid()) {
1981 Ldr(bottom_0, MemOperand(StackPointer(), size, PostIndex));
1982 }
1983 }
1984
1985
PushMultipleTimes(int count,Register src)1986 void MacroAssembler::PushMultipleTimes(int count, Register src) {
1987 VIXL_ASSERT(allow_macro_instructions_);
1988 int size = src.GetSizeInBytes();
1989
1990 PrepareForPush(count, size);
1991 // Push up to four registers at a time if possible because if the current
1992 // stack pointer is sp and the register size is 32, registers must be pushed
1993 // in blocks of four in order to maintain the 16-byte alignment for sp.
1994 while (count >= 4) {
1995 PushHelper(4, size, src, src, src, src);
1996 count -= 4;
1997 }
1998 if (count >= 2) {
1999 PushHelper(2, size, src, src, NoReg, NoReg);
2000 count -= 2;
2001 }
2002 if (count == 1) {
2003 PushHelper(1, size, src, NoReg, NoReg, NoReg);
2004 count -= 1;
2005 }
2006 VIXL_ASSERT(count == 0);
2007 }
2008
2009
PushHelper(int count,int size,const CPURegister & src0,const CPURegister & src1,const CPURegister & src2,const CPURegister & src3)2010 void MacroAssembler::PushHelper(int count,
2011 int size,
2012 const CPURegister& src0,
2013 const CPURegister& src1,
2014 const CPURegister& src2,
2015 const CPURegister& src3) {
2016 // Ensure that we don't unintentionally modify scratch or debug registers.
2017 // Worst case for size is 2 stp.
2018 ExactAssemblyScope scope(this,
2019 2 * kInstructionSize,
2020 ExactAssemblyScope::kMaximumSize);
2021
2022 VIXL_ASSERT(AreSameSizeAndType(src0, src1, src2, src3));
2023 VIXL_ASSERT(size == src0.GetSizeInBytes());
2024
2025 // When pushing multiple registers, the store order is chosen such that
2026 // Push(a, b) is equivalent to Push(a) followed by Push(b).
2027 switch (count) {
2028 case 1:
2029 VIXL_ASSERT(src1.IsNone() && src2.IsNone() && src3.IsNone());
2030 str(src0, MemOperand(StackPointer(), -1 * size, PreIndex));
2031 break;
2032 case 2:
2033 VIXL_ASSERT(src2.IsNone() && src3.IsNone());
2034 stp(src1, src0, MemOperand(StackPointer(), -2 * size, PreIndex));
2035 break;
2036 case 3:
2037 VIXL_ASSERT(src3.IsNone());
2038 stp(src2, src1, MemOperand(StackPointer(), -3 * size, PreIndex));
2039 str(src0, MemOperand(StackPointer(), 2 * size));
2040 break;
2041 case 4:
2042 // Skip over 4 * size, then fill in the gap. This allows four W registers
2043 // to be pushed using sp, whilst maintaining 16-byte alignment for sp at
2044 // all times.
2045 stp(src3, src2, MemOperand(StackPointer(), -4 * size, PreIndex));
2046 stp(src1, src0, MemOperand(StackPointer(), 2 * size));
2047 break;
2048 default:
2049 VIXL_UNREACHABLE();
2050 }
2051 }
2052
2053
PopHelper(int count,int size,const CPURegister & dst0,const CPURegister & dst1,const CPURegister & dst2,const CPURegister & dst3)2054 void MacroAssembler::PopHelper(int count,
2055 int size,
2056 const CPURegister& dst0,
2057 const CPURegister& dst1,
2058 const CPURegister& dst2,
2059 const CPURegister& dst3) {
2060 // Ensure that we don't unintentionally modify scratch or debug registers.
2061 // Worst case for size is 2 ldp.
2062 ExactAssemblyScope scope(this,
2063 2 * kInstructionSize,
2064 ExactAssemblyScope::kMaximumSize);
2065
2066 VIXL_ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3));
2067 VIXL_ASSERT(size == dst0.GetSizeInBytes());
2068
2069 // When popping multiple registers, the load order is chosen such that
2070 // Pop(a, b) is equivalent to Pop(a) followed by Pop(b).
2071 switch (count) {
2072 case 1:
2073 VIXL_ASSERT(dst1.IsNone() && dst2.IsNone() && dst3.IsNone());
2074 ldr(dst0, MemOperand(StackPointer(), 1 * size, PostIndex));
2075 break;
2076 case 2:
2077 VIXL_ASSERT(dst2.IsNone() && dst3.IsNone());
2078 ldp(dst0, dst1, MemOperand(StackPointer(), 2 * size, PostIndex));
2079 break;
2080 case 3:
2081 VIXL_ASSERT(dst3.IsNone());
2082 ldr(dst2, MemOperand(StackPointer(), 2 * size));
2083 ldp(dst0, dst1, MemOperand(StackPointer(), 3 * size, PostIndex));
2084 break;
2085 case 4:
2086 // Load the higher addresses first, then load the lower addresses and skip
2087 // the whole block in the second instruction. This allows four W registers
2088 // to be popped using sp, whilst maintaining 16-byte alignment for sp at
2089 // all times.
2090 ldp(dst2, dst3, MemOperand(StackPointer(), 2 * size));
2091 ldp(dst0, dst1, MemOperand(StackPointer(), 4 * size, PostIndex));
2092 break;
2093 default:
2094 VIXL_UNREACHABLE();
2095 }
2096 }
2097
2098
PrepareForPush(int count,int size)2099 void MacroAssembler::PrepareForPush(int count, int size) {
2100 if (sp.Is(StackPointer())) {
2101 // If the current stack pointer is sp, then it must be aligned to 16 bytes
2102 // on entry and the total size of the specified registers must also be a
2103 // multiple of 16 bytes.
2104 VIXL_ASSERT((count * size) % 16 == 0);
2105 } else {
2106 // Even if the current stack pointer is not the system stack pointer (sp),
2107 // the system stack pointer will still be modified in order to comply with
2108 // ABI rules about accessing memory below the system stack pointer.
2109 BumpSystemStackPointer(count * size);
2110 }
2111 }
2112
2113
PrepareForPop(int count,int size)2114 void MacroAssembler::PrepareForPop(int count, int size) {
2115 USE(count, size);
2116 if (sp.Is(StackPointer())) {
2117 // If the current stack pointer is sp, then it must be aligned to 16 bytes
2118 // on entry and the total size of the specified registers must also be a
2119 // multiple of 16 bytes.
2120 VIXL_ASSERT((count * size) % 16 == 0);
2121 }
2122 }
2123
Poke(const Register & src,const Operand & offset)2124 void MacroAssembler::Poke(const Register& src, const Operand& offset) {
2125 VIXL_ASSERT(allow_macro_instructions_);
2126 if (offset.IsImmediate()) {
2127 VIXL_ASSERT(offset.GetImmediate() >= 0);
2128 }
2129
2130 Str(src, MemOperand(StackPointer(), offset));
2131 }
2132
2133
Peek(const Register & dst,const Operand & offset)2134 void MacroAssembler::Peek(const Register& dst, const Operand& offset) {
2135 VIXL_ASSERT(allow_macro_instructions_);
2136 if (offset.IsImmediate()) {
2137 VIXL_ASSERT(offset.GetImmediate() >= 0);
2138 }
2139
2140 Ldr(dst, MemOperand(StackPointer(), offset));
2141 }
2142
2143
Claim(const Operand & size)2144 void MacroAssembler::Claim(const Operand& size) {
2145 VIXL_ASSERT(allow_macro_instructions_);
2146
2147 if (size.IsZero()) {
2148 return;
2149 }
2150
2151 if (size.IsImmediate()) {
2152 VIXL_ASSERT(size.GetImmediate() > 0);
2153 if (sp.Is(StackPointer())) {
2154 VIXL_ASSERT((size.GetImmediate() % 16) == 0);
2155 }
2156 }
2157
2158 if (!sp.Is(StackPointer())) {
2159 BumpSystemStackPointer(size);
2160 }
2161
2162 Sub(StackPointer(), StackPointer(), size);
2163 }
2164
2165
Drop(const Operand & size)2166 void MacroAssembler::Drop(const Operand& size) {
2167 VIXL_ASSERT(allow_macro_instructions_);
2168
2169 if (size.IsZero()) {
2170 return;
2171 }
2172
2173 if (size.IsImmediate()) {
2174 VIXL_ASSERT(size.GetImmediate() > 0);
2175 if (sp.Is(StackPointer())) {
2176 VIXL_ASSERT((size.GetImmediate() % 16) == 0);
2177 }
2178 }
2179
2180 Add(StackPointer(), StackPointer(), size);
2181 }
2182
2183
PushCalleeSavedRegisters()2184 void MacroAssembler::PushCalleeSavedRegisters() {
2185 // Ensure that the macro-assembler doesn't use any scratch registers.
2186 // 10 stp will be emitted.
2187 // TODO(all): Should we use GetCalleeSaved and SavedFP.
2188 ExactAssemblyScope scope(this, 10 * kInstructionSize);
2189
2190 // This method must not be called unless the current stack pointer is sp.
2191 VIXL_ASSERT(sp.Is(StackPointer()));
2192
2193 MemOperand tos(sp, -2 * static_cast<int>(kXRegSizeInBytes), PreIndex);
2194
2195 stp(x29, x30, tos);
2196 stp(x27, x28, tos);
2197 stp(x25, x26, tos);
2198 stp(x23, x24, tos);
2199 stp(x21, x22, tos);
2200 stp(x19, x20, tos);
2201
2202 stp(d14, d15, tos);
2203 stp(d12, d13, tos);
2204 stp(d10, d11, tos);
2205 stp(d8, d9, tos);
2206 }
2207
2208
PopCalleeSavedRegisters()2209 void MacroAssembler::PopCalleeSavedRegisters() {
2210 // Ensure that the macro-assembler doesn't use any scratch registers.
2211 // 10 ldp will be emitted.
2212 // TODO(all): Should we use GetCalleeSaved and SavedFP.
2213 ExactAssemblyScope scope(this, 10 * kInstructionSize);
2214
2215 // This method must not be called unless the current stack pointer is sp.
2216 VIXL_ASSERT(sp.Is(StackPointer()));
2217
2218 MemOperand tos(sp, 2 * kXRegSizeInBytes, PostIndex);
2219
2220 ldp(d8, d9, tos);
2221 ldp(d10, d11, tos);
2222 ldp(d12, d13, tos);
2223 ldp(d14, d15, tos);
2224
2225 ldp(x19, x20, tos);
2226 ldp(x21, x22, tos);
2227 ldp(x23, x24, tos);
2228 ldp(x25, x26, tos);
2229 ldp(x27, x28, tos);
2230 ldp(x29, x30, tos);
2231 }
2232
LoadCPURegList(CPURegList registers,const MemOperand & src)2233 void MacroAssembler::LoadCPURegList(CPURegList registers,
2234 const MemOperand& src) {
2235 LoadStoreCPURegListHelper(kLoad, registers, src);
2236 }
2237
StoreCPURegList(CPURegList registers,const MemOperand & dst)2238 void MacroAssembler::StoreCPURegList(CPURegList registers,
2239 const MemOperand& dst) {
2240 LoadStoreCPURegListHelper(kStore, registers, dst);
2241 }
2242
2243
LoadStoreCPURegListHelper(LoadStoreCPURegListAction op,CPURegList registers,const MemOperand & mem)2244 void MacroAssembler::LoadStoreCPURegListHelper(LoadStoreCPURegListAction op,
2245 CPURegList registers,
2246 const MemOperand& mem) {
2247 // We do not handle pre-indexing or post-indexing.
2248 VIXL_ASSERT(!(mem.IsPreIndex() || mem.IsPostIndex()));
2249 VIXL_ASSERT(!registers.Overlaps(tmp_list_));
2250 VIXL_ASSERT(!registers.Overlaps(fptmp_list_));
2251 VIXL_ASSERT(!registers.IncludesAliasOf(sp));
2252
2253 UseScratchRegisterScope temps(this);
2254
2255 MemOperand loc = BaseMemOperandForLoadStoreCPURegList(registers, mem, &temps);
2256 const int reg_size = registers.GetRegisterSizeInBytes();
2257
2258 VIXL_ASSERT(IsPowerOf2(reg_size));
2259
2260 // Since we are operating on register pairs, we would like to align on double
2261 // the standard size; on the other hand, we don't want to insert an extra
2262 // operation, which will happen if the number of registers is even. Note that
2263 // the alignment of the base pointer is unknown here, but we assume that it
2264 // is more likely to be aligned.
2265 if (((loc.GetOffset() & (2 * reg_size - 1)) != 0) &&
2266 ((registers.GetCount() % 2) != 0)) {
2267 if (op == kStore) {
2268 Str(registers.PopLowestIndex(), loc);
2269 } else {
2270 VIXL_ASSERT(op == kLoad);
2271 Ldr(registers.PopLowestIndex(), loc);
2272 }
2273 loc.AddOffset(reg_size);
2274 }
2275 while (registers.GetCount() >= 2) {
2276 const CPURegister& dst0 = registers.PopLowestIndex();
2277 const CPURegister& dst1 = registers.PopLowestIndex();
2278 if (op == kStore) {
2279 Stp(dst0, dst1, loc);
2280 } else {
2281 VIXL_ASSERT(op == kLoad);
2282 Ldp(dst0, dst1, loc);
2283 }
2284 loc.AddOffset(2 * reg_size);
2285 }
2286 if (!registers.IsEmpty()) {
2287 if (op == kStore) {
2288 Str(registers.PopLowestIndex(), loc);
2289 } else {
2290 VIXL_ASSERT(op == kLoad);
2291 Ldr(registers.PopLowestIndex(), loc);
2292 }
2293 }
2294 }
2295
BaseMemOperandForLoadStoreCPURegList(const CPURegList & registers,const MemOperand & mem,UseScratchRegisterScope * scratch_scope)2296 MemOperand MacroAssembler::BaseMemOperandForLoadStoreCPURegList(
2297 const CPURegList& registers,
2298 const MemOperand& mem,
2299 UseScratchRegisterScope* scratch_scope) {
2300 // If necessary, pre-compute the base address for the accesses.
2301 if (mem.IsRegisterOffset()) {
2302 Register reg_base = scratch_scope->AcquireX();
2303 ComputeAddress(reg_base, mem);
2304 return MemOperand(reg_base);
2305
2306 } else if (mem.IsImmediateOffset()) {
2307 int reg_size = registers.GetRegisterSizeInBytes();
2308 int total_size = registers.GetTotalSizeInBytes();
2309 int64_t min_offset = mem.GetOffset();
2310 int64_t max_offset =
2311 mem.GetOffset() + std::max(0, total_size - 2 * reg_size);
2312 if ((registers.GetCount() >= 2) &&
2313 (!Assembler::IsImmLSPair(min_offset, WhichPowerOf2(reg_size)) ||
2314 !Assembler::IsImmLSPair(max_offset, WhichPowerOf2(reg_size)))) {
2315 Register reg_base = scratch_scope->AcquireX();
2316 ComputeAddress(reg_base, mem);
2317 return MemOperand(reg_base);
2318 }
2319 }
2320
2321 return mem;
2322 }
2323
BumpSystemStackPointer(const Operand & space)2324 void MacroAssembler::BumpSystemStackPointer(const Operand& space) {
2325 VIXL_ASSERT(!sp.Is(StackPointer()));
2326 // TODO: Several callers rely on this not using scratch registers, so we use
2327 // the assembler directly here. However, this means that large immediate
2328 // values of 'space' cannot be handled.
2329 ExactAssemblyScope scope(this, kInstructionSize);
2330 sub(sp, StackPointer(), space);
2331 }
2332
2333
2334 // TODO(all): Fix printf for NEON registers, and resolve whether we should be
2335 // using FPRegister or VRegister here.
2336
2337 // This is the main Printf implementation. All callee-saved registers are
2338 // preserved, but NZCV and the caller-saved registers may be clobbered.
PrintfNoPreserve(const char * format,const CPURegister & arg0,const CPURegister & arg1,const CPURegister & arg2,const CPURegister & arg3)2339 void MacroAssembler::PrintfNoPreserve(const char* format,
2340 const CPURegister& arg0,
2341 const CPURegister& arg1,
2342 const CPURegister& arg2,
2343 const CPURegister& arg3) {
2344 // We cannot handle a caller-saved stack pointer. It doesn't make much sense
2345 // in most cases anyway, so this restriction shouldn't be too serious.
2346 VIXL_ASSERT(!kCallerSaved.IncludesAliasOf(StackPointer()));
2347
2348 // The provided arguments, and their proper PCS registers.
2349 CPURegister args[kPrintfMaxArgCount] = {arg0, arg1, arg2, arg3};
2350 CPURegister pcs[kPrintfMaxArgCount];
2351
2352 int arg_count = kPrintfMaxArgCount;
2353
2354 // The PCS varargs registers for printf. Note that x0 is used for the printf
2355 // format string.
2356 static const CPURegList kPCSVarargs =
2357 CPURegList(CPURegister::kRegister, kXRegSize, 1, arg_count);
2358 static const CPURegList kPCSVarargsFP =
2359 CPURegList(CPURegister::kVRegister, kDRegSize, 0, arg_count - 1);
2360
2361 // We can use caller-saved registers as scratch values, except for the
2362 // arguments and the PCS registers where they might need to go.
2363 UseScratchRegisterScope temps(this);
2364 temps.Include(kCallerSaved);
2365 temps.Include(kCallerSavedV);
2366 temps.Exclude(kPCSVarargs);
2367 temps.Exclude(kPCSVarargsFP);
2368 temps.Exclude(arg0, arg1, arg2, arg3);
2369
2370 // Copies of the arg lists that we can iterate through.
2371 CPURegList pcs_varargs = kPCSVarargs;
2372 CPURegList pcs_varargs_fp = kPCSVarargsFP;
2373
2374 // Place the arguments. There are lots of clever tricks and optimizations we
2375 // could use here, but Printf is a debug tool so instead we just try to keep
2376 // it simple: Move each input that isn't already in the right place to a
2377 // scratch register, then move everything back.
2378 for (unsigned i = 0; i < kPrintfMaxArgCount; i++) {
2379 // Work out the proper PCS register for this argument.
2380 if (args[i].IsRegister()) {
2381 pcs[i] = pcs_varargs.PopLowestIndex().X();
2382 // We might only need a W register here. We need to know the size of the
2383 // argument so we can properly encode it for the simulator call.
2384 if (args[i].Is32Bits()) pcs[i] = pcs[i].W();
2385 } else if (args[i].IsVRegister()) {
2386 // In C, floats are always cast to doubles for varargs calls.
2387 pcs[i] = pcs_varargs_fp.PopLowestIndex().D();
2388 } else {
2389 VIXL_ASSERT(args[i].IsNone());
2390 arg_count = i;
2391 break;
2392 }
2393
2394 // If the argument is already in the right place, leave it where it is.
2395 if (args[i].Aliases(pcs[i])) continue;
2396
2397 // Otherwise, if the argument is in a PCS argument register, allocate an
2398 // appropriate scratch register and then move it out of the way.
2399 if (kPCSVarargs.IncludesAliasOf(args[i]) ||
2400 kPCSVarargsFP.IncludesAliasOf(args[i])) {
2401 if (args[i].IsRegister()) {
2402 Register old_arg = Register(args[i]);
2403 Register new_arg = temps.AcquireSameSizeAs(old_arg);
2404 Mov(new_arg, old_arg);
2405 args[i] = new_arg;
2406 } else {
2407 FPRegister old_arg = FPRegister(args[i]);
2408 FPRegister new_arg = temps.AcquireSameSizeAs(old_arg);
2409 Fmov(new_arg, old_arg);
2410 args[i] = new_arg;
2411 }
2412 }
2413 }
2414
2415 // Do a second pass to move values into their final positions and perform any
2416 // conversions that may be required.
2417 for (int i = 0; i < arg_count; i++) {
2418 VIXL_ASSERT(pcs[i].GetType() == args[i].GetType());
2419 if (pcs[i].IsRegister()) {
2420 Mov(Register(pcs[i]), Register(args[i]), kDiscardForSameWReg);
2421 } else {
2422 VIXL_ASSERT(pcs[i].IsVRegister());
2423 if (pcs[i].GetSizeInBits() == args[i].GetSizeInBits()) {
2424 Fmov(FPRegister(pcs[i]), FPRegister(args[i]));
2425 } else {
2426 Fcvt(FPRegister(pcs[i]), FPRegister(args[i]));
2427 }
2428 }
2429 }
2430
2431 // Load the format string into x0, as per the procedure-call standard.
2432 //
2433 // To make the code as portable as possible, the format string is encoded
2434 // directly in the instruction stream. It might be cleaner to encode it in a
2435 // literal pool, but since Printf is usually used for debugging, it is
2436 // beneficial for it to be minimally dependent on other features.
2437 temps.Exclude(x0);
2438 Label format_address;
2439 Adr(x0, &format_address);
2440
2441 // Emit the format string directly in the instruction stream.
2442 {
2443 BlockPoolsScope scope(this);
2444 // Data emitted:
2445 // branch
2446 // strlen(format) + 1 (includes null termination)
2447 // padding to next instruction
2448 // unreachable
2449 EmissionCheckScope guard(this,
2450 AlignUp(strlen(format) + 1, kInstructionSize) +
2451 2 * kInstructionSize);
2452 Label after_data;
2453 B(&after_data);
2454 Bind(&format_address);
2455 EmitString(format);
2456 Unreachable();
2457 Bind(&after_data);
2458 }
2459
2460 // We don't pass any arguments on the stack, but we still need to align the C
2461 // stack pointer to a 16-byte boundary for PCS compliance.
2462 if (!sp.Is(StackPointer())) {
2463 Bic(sp, StackPointer(), 0xf);
2464 }
2465
2466 // Actually call printf. This part needs special handling for the simulator,
2467 // since the system printf function will use a different instruction set and
2468 // the procedure-call standard will not be compatible.
2469 if (generate_simulator_code_) {
2470 ExactAssemblyScope scope(this, kPrintfLength);
2471 hlt(kPrintfOpcode);
2472 dc32(arg_count); // kPrintfArgCountOffset
2473
2474 // Determine the argument pattern.
2475 uint32_t arg_pattern_list = 0;
2476 for (int i = 0; i < arg_count; i++) {
2477 uint32_t arg_pattern;
2478 if (pcs[i].IsRegister()) {
2479 arg_pattern = pcs[i].Is32Bits() ? kPrintfArgW : kPrintfArgX;
2480 } else {
2481 VIXL_ASSERT(pcs[i].Is64Bits());
2482 arg_pattern = kPrintfArgD;
2483 }
2484 VIXL_ASSERT(arg_pattern < (1 << kPrintfArgPatternBits));
2485 arg_pattern_list |= (arg_pattern << (kPrintfArgPatternBits * i));
2486 }
2487 dc32(arg_pattern_list); // kPrintfArgPatternListOffset
2488 } else {
2489 Register tmp = temps.AcquireX();
2490 Mov(tmp, reinterpret_cast<uintptr_t>(printf));
2491 Blr(tmp);
2492 }
2493 }
2494
2495
Printf(const char * format,CPURegister arg0,CPURegister arg1,CPURegister arg2,CPURegister arg3)2496 void MacroAssembler::Printf(const char* format,
2497 CPURegister arg0,
2498 CPURegister arg1,
2499 CPURegister arg2,
2500 CPURegister arg3) {
2501 // We can only print sp if it is the current stack pointer.
2502 if (!sp.Is(StackPointer())) {
2503 VIXL_ASSERT(!sp.Aliases(arg0));
2504 VIXL_ASSERT(!sp.Aliases(arg1));
2505 VIXL_ASSERT(!sp.Aliases(arg2));
2506 VIXL_ASSERT(!sp.Aliases(arg3));
2507 }
2508
2509 // Make sure that the macro assembler doesn't try to use any of our arguments
2510 // as scratch registers.
2511 UseScratchRegisterScope exclude_all(this);
2512 exclude_all.ExcludeAll();
2513
2514 // Preserve all caller-saved registers as well as NZCV.
2515 // If sp is the stack pointer, PushCPURegList asserts that the size of each
2516 // list is a multiple of 16 bytes.
2517 PushCPURegList(kCallerSaved);
2518 PushCPURegList(kCallerSavedV);
2519
2520 {
2521 UseScratchRegisterScope temps(this);
2522 // We can use caller-saved registers as scratch values (except for argN).
2523 temps.Include(kCallerSaved);
2524 temps.Include(kCallerSavedV);
2525 temps.Exclude(arg0, arg1, arg2, arg3);
2526
2527 // If any of the arguments are the current stack pointer, allocate a new
2528 // register for them, and adjust the value to compensate for pushing the
2529 // caller-saved registers.
2530 bool arg0_sp = StackPointer().Aliases(arg0);
2531 bool arg1_sp = StackPointer().Aliases(arg1);
2532 bool arg2_sp = StackPointer().Aliases(arg2);
2533 bool arg3_sp = StackPointer().Aliases(arg3);
2534 if (arg0_sp || arg1_sp || arg2_sp || arg3_sp) {
2535 // Allocate a register to hold the original stack pointer value, to pass
2536 // to PrintfNoPreserve as an argument.
2537 Register arg_sp = temps.AcquireX();
2538 Add(arg_sp,
2539 StackPointer(),
2540 kCallerSaved.GetTotalSizeInBytes() +
2541 kCallerSavedV.GetTotalSizeInBytes());
2542 if (arg0_sp) arg0 = Register(arg_sp.GetCode(), arg0.GetSizeInBits());
2543 if (arg1_sp) arg1 = Register(arg_sp.GetCode(), arg1.GetSizeInBits());
2544 if (arg2_sp) arg2 = Register(arg_sp.GetCode(), arg2.GetSizeInBits());
2545 if (arg3_sp) arg3 = Register(arg_sp.GetCode(), arg3.GetSizeInBits());
2546 }
2547
2548 // Preserve NZCV.
2549 Register tmp = temps.AcquireX();
2550 Mrs(tmp, NZCV);
2551 Push(tmp, xzr);
2552 temps.Release(tmp);
2553
2554 PrintfNoPreserve(format, arg0, arg1, arg2, arg3);
2555
2556 // Restore NZCV.
2557 tmp = temps.AcquireX();
2558 Pop(xzr, tmp);
2559 Msr(NZCV, tmp);
2560 temps.Release(tmp);
2561 }
2562
2563 PopCPURegList(kCallerSavedV);
2564 PopCPURegList(kCallerSaved);
2565 }
2566
Trace(TraceParameters parameters,TraceCommand command)2567 void MacroAssembler::Trace(TraceParameters parameters, TraceCommand command) {
2568 VIXL_ASSERT(allow_macro_instructions_);
2569
2570 if (generate_simulator_code_) {
2571 // The arguments to the trace pseudo instruction need to be contiguous in
2572 // memory, so make sure we don't try to emit a literal pool.
2573 ExactAssemblyScope scope(this, kTraceLength);
2574
2575 Label start;
2576 bind(&start);
2577
2578 // Refer to simulator-aarch64.h for a description of the marker and its
2579 // arguments.
2580 hlt(kTraceOpcode);
2581
2582 VIXL_ASSERT(GetSizeOfCodeGeneratedSince(&start) == kTraceParamsOffset);
2583 dc32(parameters);
2584
2585 VIXL_ASSERT(GetSizeOfCodeGeneratedSince(&start) == kTraceCommandOffset);
2586 dc32(command);
2587 } else {
2588 // Emit nothing on real hardware.
2589 USE(parameters, command);
2590 }
2591 }
2592
2593
Log(TraceParameters parameters)2594 void MacroAssembler::Log(TraceParameters parameters) {
2595 VIXL_ASSERT(allow_macro_instructions_);
2596
2597 if (generate_simulator_code_) {
2598 // The arguments to the log pseudo instruction need to be contiguous in
2599 // memory, so make sure we don't try to emit a literal pool.
2600 ExactAssemblyScope scope(this, kLogLength);
2601
2602 Label start;
2603 bind(&start);
2604
2605 // Refer to simulator-aarch64.h for a description of the marker and its
2606 // arguments.
2607 hlt(kLogOpcode);
2608
2609 VIXL_ASSERT(GetSizeOfCodeGeneratedSince(&start) == kLogParamsOffset);
2610 dc32(parameters);
2611 } else {
2612 // Emit nothing on real hardware.
2613 USE(parameters);
2614 }
2615 }
2616
2617
EnableInstrumentation()2618 void MacroAssembler::EnableInstrumentation() {
2619 VIXL_ASSERT(!isprint(InstrumentStateEnable));
2620 ExactAssemblyScope scope(this, kInstructionSize);
2621 movn(xzr, InstrumentStateEnable);
2622 }
2623
2624
DisableInstrumentation()2625 void MacroAssembler::DisableInstrumentation() {
2626 VIXL_ASSERT(!isprint(InstrumentStateDisable));
2627 ExactAssemblyScope scope(this, kInstructionSize);
2628 movn(xzr, InstrumentStateDisable);
2629 }
2630
2631
AnnotateInstrumentation(const char * marker_name)2632 void MacroAssembler::AnnotateInstrumentation(const char* marker_name) {
2633 VIXL_ASSERT(strlen(marker_name) == 2);
2634
2635 // We allow only printable characters in the marker names. Unprintable
2636 // characters are reserved for controlling features of the instrumentation.
2637 VIXL_ASSERT(isprint(marker_name[0]) && isprint(marker_name[1]));
2638
2639 ExactAssemblyScope scope(this, kInstructionSize);
2640 movn(xzr, (marker_name[1] << 8) | marker_name[0]);
2641 }
2642
2643
Open(MacroAssembler * masm)2644 void UseScratchRegisterScope::Open(MacroAssembler* masm) {
2645 VIXL_ASSERT(masm_ == NULL);
2646 VIXL_ASSERT(masm != NULL);
2647 masm_ = masm;
2648
2649 CPURegList* available = masm->GetScratchRegisterList();
2650 CPURegList* available_fp = masm->GetScratchFPRegisterList();
2651 old_available_ = available->GetList();
2652 old_availablefp_ = available_fp->GetList();
2653 VIXL_ASSERT(available->GetType() == CPURegister::kRegister);
2654 VIXL_ASSERT(available_fp->GetType() == CPURegister::kVRegister);
2655
2656 parent_ = masm->GetCurrentScratchRegisterScope();
2657 masm->SetCurrentScratchRegisterScope(this);
2658 }
2659
2660
Close()2661 void UseScratchRegisterScope::Close() {
2662 if (masm_ != NULL) {
2663 // Ensure that scopes nest perfectly, and do not outlive their parents.
2664 // This is a run-time check because the order of destruction of objects in
2665 // the _same_ scope is implementation-defined, and is likely to change in
2666 // optimised builds.
2667 VIXL_CHECK(masm_->GetCurrentScratchRegisterScope() == this);
2668 masm_->SetCurrentScratchRegisterScope(parent_);
2669
2670 masm_->GetScratchRegisterList()->SetList(old_available_);
2671 masm_->GetScratchFPRegisterList()->SetList(old_availablefp_);
2672
2673 masm_ = NULL;
2674 }
2675 }
2676
2677
IsAvailable(const CPURegister & reg) const2678 bool UseScratchRegisterScope::IsAvailable(const CPURegister& reg) const {
2679 return masm_->GetScratchRegisterList()->IncludesAliasOf(reg) ||
2680 masm_->GetScratchFPRegisterList()->IncludesAliasOf(reg);
2681 }
2682
2683
AcquireRegisterOfSize(int size_in_bits)2684 Register UseScratchRegisterScope::AcquireRegisterOfSize(int size_in_bits) {
2685 int code = AcquireNextAvailable(masm_->GetScratchRegisterList()).GetCode();
2686 return Register(code, size_in_bits);
2687 }
2688
2689
AcquireVRegisterOfSize(int size_in_bits)2690 FPRegister UseScratchRegisterScope::AcquireVRegisterOfSize(int size_in_bits) {
2691 int code = AcquireNextAvailable(masm_->GetScratchFPRegisterList()).GetCode();
2692 return FPRegister(code, size_in_bits);
2693 }
2694
2695
Release(const CPURegister & reg)2696 void UseScratchRegisterScope::Release(const CPURegister& reg) {
2697 VIXL_ASSERT(masm_ != NULL);
2698 if (reg.IsRegister()) {
2699 ReleaseByCode(masm_->GetScratchRegisterList(), reg.GetCode());
2700 } else if (reg.IsVRegister()) {
2701 ReleaseByCode(masm_->GetScratchFPRegisterList(), reg.GetCode());
2702 } else {
2703 VIXL_ASSERT(reg.IsNone());
2704 }
2705 }
2706
2707
Include(const CPURegList & list)2708 void UseScratchRegisterScope::Include(const CPURegList& list) {
2709 VIXL_ASSERT(masm_ != NULL);
2710 if (list.GetType() == CPURegister::kRegister) {
2711 // Make sure that neither sp nor xzr are included the list.
2712 IncludeByRegList(masm_->GetScratchRegisterList(),
2713 list.GetList() & ~(xzr.GetBit() | sp.GetBit()));
2714 } else {
2715 VIXL_ASSERT(list.GetType() == CPURegister::kVRegister);
2716 IncludeByRegList(masm_->GetScratchFPRegisterList(), list.GetList());
2717 }
2718 }
2719
2720
Include(const Register & reg1,const Register & reg2,const Register & reg3,const Register & reg4)2721 void UseScratchRegisterScope::Include(const Register& reg1,
2722 const Register& reg2,
2723 const Register& reg3,
2724 const Register& reg4) {
2725 VIXL_ASSERT(masm_ != NULL);
2726 RegList include =
2727 reg1.GetBit() | reg2.GetBit() | reg3.GetBit() | reg4.GetBit();
2728 // Make sure that neither sp nor xzr are included the list.
2729 include &= ~(xzr.GetBit() | sp.GetBit());
2730
2731 IncludeByRegList(masm_->GetScratchRegisterList(), include);
2732 }
2733
2734
Include(const FPRegister & reg1,const FPRegister & reg2,const FPRegister & reg3,const FPRegister & reg4)2735 void UseScratchRegisterScope::Include(const FPRegister& reg1,
2736 const FPRegister& reg2,
2737 const FPRegister& reg3,
2738 const FPRegister& reg4) {
2739 RegList include =
2740 reg1.GetBit() | reg2.GetBit() | reg3.GetBit() | reg4.GetBit();
2741 IncludeByRegList(masm_->GetScratchFPRegisterList(), include);
2742 }
2743
2744
Exclude(const CPURegList & list)2745 void UseScratchRegisterScope::Exclude(const CPURegList& list) {
2746 if (list.GetType() == CPURegister::kRegister) {
2747 ExcludeByRegList(masm_->GetScratchRegisterList(), list.GetList());
2748 } else {
2749 VIXL_ASSERT(list.GetType() == CPURegister::kVRegister);
2750 ExcludeByRegList(masm_->GetScratchFPRegisterList(), list.GetList());
2751 }
2752 }
2753
2754
Exclude(const Register & reg1,const Register & reg2,const Register & reg3,const Register & reg4)2755 void UseScratchRegisterScope::Exclude(const Register& reg1,
2756 const Register& reg2,
2757 const Register& reg3,
2758 const Register& reg4) {
2759 RegList exclude =
2760 reg1.GetBit() | reg2.GetBit() | reg3.GetBit() | reg4.GetBit();
2761 ExcludeByRegList(masm_->GetScratchRegisterList(), exclude);
2762 }
2763
2764
Exclude(const FPRegister & reg1,const FPRegister & reg2,const FPRegister & reg3,const FPRegister & reg4)2765 void UseScratchRegisterScope::Exclude(const FPRegister& reg1,
2766 const FPRegister& reg2,
2767 const FPRegister& reg3,
2768 const FPRegister& reg4) {
2769 RegList excludefp =
2770 reg1.GetBit() | reg2.GetBit() | reg3.GetBit() | reg4.GetBit();
2771 ExcludeByRegList(masm_->GetScratchFPRegisterList(), excludefp);
2772 }
2773
2774
Exclude(const CPURegister & reg1,const CPURegister & reg2,const CPURegister & reg3,const CPURegister & reg4)2775 void UseScratchRegisterScope::Exclude(const CPURegister& reg1,
2776 const CPURegister& reg2,
2777 const CPURegister& reg3,
2778 const CPURegister& reg4) {
2779 RegList exclude = 0;
2780 RegList excludefp = 0;
2781
2782 const CPURegister regs[] = {reg1, reg2, reg3, reg4};
2783
2784 for (unsigned i = 0; i < (sizeof(regs) / sizeof(regs[0])); i++) {
2785 if (regs[i].IsRegister()) {
2786 exclude |= regs[i].GetBit();
2787 } else if (regs[i].IsFPRegister()) {
2788 excludefp |= regs[i].GetBit();
2789 } else {
2790 VIXL_ASSERT(regs[i].IsNone());
2791 }
2792 }
2793
2794 ExcludeByRegList(masm_->GetScratchRegisterList(), exclude);
2795 ExcludeByRegList(masm_->GetScratchFPRegisterList(), excludefp);
2796 }
2797
2798
ExcludeAll()2799 void UseScratchRegisterScope::ExcludeAll() {
2800 ExcludeByRegList(masm_->GetScratchRegisterList(),
2801 masm_->GetScratchRegisterList()->GetList());
2802 ExcludeByRegList(masm_->GetScratchFPRegisterList(),
2803 masm_->GetScratchFPRegisterList()->GetList());
2804 }
2805
2806
AcquireNextAvailable(CPURegList * available)2807 CPURegister UseScratchRegisterScope::AcquireNextAvailable(
2808 CPURegList* available) {
2809 VIXL_CHECK(!available->IsEmpty());
2810 CPURegister result = available->PopLowestIndex();
2811 VIXL_ASSERT(!AreAliased(result, xzr, sp));
2812 return result;
2813 }
2814
2815
ReleaseByCode(CPURegList * available,int code)2816 void UseScratchRegisterScope::ReleaseByCode(CPURegList* available, int code) {
2817 ReleaseByRegList(available, static_cast<RegList>(1) << code);
2818 }
2819
2820
ReleaseByRegList(CPURegList * available,RegList regs)2821 void UseScratchRegisterScope::ReleaseByRegList(CPURegList* available,
2822 RegList regs) {
2823 available->SetList(available->GetList() | regs);
2824 }
2825
2826
IncludeByRegList(CPURegList * available,RegList regs)2827 void UseScratchRegisterScope::IncludeByRegList(CPURegList* available,
2828 RegList regs) {
2829 available->SetList(available->GetList() | regs);
2830 }
2831
2832
ExcludeByRegList(CPURegList * available,RegList exclude)2833 void UseScratchRegisterScope::ExcludeByRegList(CPURegList* available,
2834 RegList exclude) {
2835 available->SetList(available->GetList() & ~exclude);
2836 }
2837
2838 } // namespace aarch64
2839 } // namespace vixl
2840