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