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