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