Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/vixl/src/aarch32/macro-assembler-aarch32.cc
4261 views
1
// Copyright 2017, 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
10
// notice, this list of conditions and the following disclaimer in the
11
// documentation and/or other materials provided with the distribution.
12
// * Neither the name of ARM Limited nor the names of its contributors may
13
// be used to endorse or promote products derived from this software
14
// without 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
18
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
// POSSIBILITY OF SUCH DAMAGE.
27
28
#include "aarch32/macro-assembler-aarch32.h"
29
30
#define STRINGIFY(x) #x
31
#define TOSTRING(x) STRINGIFY(x)
32
33
#define CONTEXT_SCOPE \
34
ContextScope context(this, __FILE__ ":" TOSTRING(__LINE__))
35
36
namespace vixl {
37
namespace aarch32 {
38
39
ExactAssemblyScopeWithoutPoolsCheck::ExactAssemblyScopeWithoutPoolsCheck(
40
MacroAssembler* masm, size_t size, SizePolicy size_policy)
41
: ExactAssemblyScope(masm,
42
size,
43
size_policy,
44
ExactAssemblyScope::kIgnorePools) {}
45
46
void UseScratchRegisterScope::Open(MacroAssembler* masm) {
47
VIXL_ASSERT(masm_ == NULL);
48
VIXL_ASSERT(masm != NULL);
49
masm_ = masm;
50
51
old_available_ = masm_->GetScratchRegisterList()->GetList();
52
old_available_vfp_ = masm_->GetScratchVRegisterList()->GetList();
53
54
parent_ = masm->GetCurrentScratchRegisterScope();
55
masm->SetCurrentScratchRegisterScope(this);
56
}
57
58
59
void UseScratchRegisterScope::Close() {
60
if (masm_ != NULL) {
61
// Ensure that scopes nest perfectly, and do not outlive their parents.
62
// This is a run-time check because the order of destruction of objects in
63
// the _same_ scope is implementation-defined, and is likely to change in
64
// optimised builds.
65
VIXL_CHECK(masm_->GetCurrentScratchRegisterScope() == this);
66
masm_->SetCurrentScratchRegisterScope(parent_);
67
68
masm_->GetScratchRegisterList()->SetList(old_available_);
69
masm_->GetScratchVRegisterList()->SetList(old_available_vfp_);
70
71
masm_ = NULL;
72
}
73
}
74
75
76
bool UseScratchRegisterScope::IsAvailable(const Register& reg) const {
77
VIXL_ASSERT(masm_ != NULL);
78
VIXL_ASSERT(reg.IsValid());
79
return masm_->GetScratchRegisterList()->Includes(reg);
80
}
81
82
83
bool UseScratchRegisterScope::IsAvailable(const VRegister& reg) const {
84
VIXL_ASSERT(masm_ != NULL);
85
VIXL_ASSERT(reg.IsValid());
86
return masm_->GetScratchVRegisterList()->IncludesAllOf(reg);
87
}
88
89
90
Register UseScratchRegisterScope::Acquire() {
91
VIXL_ASSERT(masm_ != NULL);
92
Register reg = masm_->GetScratchRegisterList()->GetFirstAvailableRegister();
93
VIXL_CHECK(reg.IsValid());
94
masm_->GetScratchRegisterList()->Remove(reg);
95
return reg;
96
}
97
98
99
VRegister UseScratchRegisterScope::AcquireV(unsigned size_in_bits) {
100
switch (size_in_bits) {
101
case kSRegSizeInBits:
102
return AcquireS();
103
case kDRegSizeInBits:
104
return AcquireD();
105
case kQRegSizeInBits:
106
return AcquireQ();
107
default:
108
VIXL_UNREACHABLE();
109
return NoVReg;
110
}
111
}
112
113
114
QRegister UseScratchRegisterScope::AcquireQ() {
115
VIXL_ASSERT(masm_ != NULL);
116
QRegister reg =
117
masm_->GetScratchVRegisterList()->GetFirstAvailableQRegister();
118
VIXL_CHECK(reg.IsValid());
119
masm_->GetScratchVRegisterList()->Remove(reg);
120
return reg;
121
}
122
123
124
DRegister UseScratchRegisterScope::AcquireD() {
125
VIXL_ASSERT(masm_ != NULL);
126
DRegister reg =
127
masm_->GetScratchVRegisterList()->GetFirstAvailableDRegister();
128
VIXL_CHECK(reg.IsValid());
129
masm_->GetScratchVRegisterList()->Remove(reg);
130
return reg;
131
}
132
133
134
SRegister UseScratchRegisterScope::AcquireS() {
135
VIXL_ASSERT(masm_ != NULL);
136
SRegister reg =
137
masm_->GetScratchVRegisterList()->GetFirstAvailableSRegister();
138
VIXL_CHECK(reg.IsValid());
139
masm_->GetScratchVRegisterList()->Remove(reg);
140
return reg;
141
}
142
143
144
void UseScratchRegisterScope::Release(const Register& reg) {
145
VIXL_ASSERT(masm_ != NULL);
146
VIXL_ASSERT(reg.IsValid());
147
VIXL_ASSERT(!masm_->GetScratchRegisterList()->Includes(reg));
148
masm_->GetScratchRegisterList()->Combine(reg);
149
}
150
151
152
void UseScratchRegisterScope::Release(const VRegister& reg) {
153
VIXL_ASSERT(masm_ != NULL);
154
VIXL_ASSERT(reg.IsValid());
155
VIXL_ASSERT(!masm_->GetScratchVRegisterList()->IncludesAliasOf(reg));
156
masm_->GetScratchVRegisterList()->Combine(reg);
157
}
158
159
160
void UseScratchRegisterScope::Include(const RegisterList& list) {
161
VIXL_ASSERT(masm_ != NULL);
162
RegisterList excluded_registers(sp, lr, pc);
163
uint32_t mask = list.GetList() & ~excluded_registers.GetList();
164
RegisterList* available = masm_->GetScratchRegisterList();
165
available->SetList(available->GetList() | mask);
166
}
167
168
169
void UseScratchRegisterScope::Include(const VRegisterList& list) {
170
VIXL_ASSERT(masm_ != NULL);
171
VRegisterList* available = masm_->GetScratchVRegisterList();
172
available->SetList(available->GetList() | list.GetList());
173
}
174
175
176
void UseScratchRegisterScope::Exclude(const RegisterList& list) {
177
VIXL_ASSERT(masm_ != NULL);
178
RegisterList* available = masm_->GetScratchRegisterList();
179
available->SetList(available->GetList() & ~list.GetList());
180
}
181
182
183
void UseScratchRegisterScope::Exclude(const VRegisterList& list) {
184
VIXL_ASSERT(masm_ != NULL);
185
VRegisterList* available = masm_->GetScratchVRegisterList();
186
available->SetList(available->GetList() & ~list.GetList());
187
}
188
189
190
void UseScratchRegisterScope::Exclude(const Operand& operand) {
191
if (operand.IsImmediateShiftedRegister()) {
192
Exclude(operand.GetBaseRegister());
193
} else if (operand.IsRegisterShiftedRegister()) {
194
Exclude(operand.GetBaseRegister(), operand.GetShiftRegister());
195
} else {
196
VIXL_ASSERT(operand.IsImmediate());
197
}
198
}
199
200
201
void UseScratchRegisterScope::ExcludeAll() {
202
VIXL_ASSERT(masm_ != NULL);
203
masm_->GetScratchRegisterList()->SetList(0);
204
masm_->GetScratchVRegisterList()->SetList(0);
205
}
206
207
208
void MacroAssembler::EnsureEmitPoolsFor(size_t size_arg) {
209
// We skip the check when the pools are blocked.
210
if (ArePoolsBlocked()) return;
211
212
VIXL_ASSERT(IsUint32(size_arg));
213
uint32_t size = static_cast<uint32_t>(size_arg);
214
215
if (pool_manager_.MustEmit(GetCursorOffset(), size)) {
216
int32_t new_pc = pool_manager_.Emit(this, GetCursorOffset(), size);
217
VIXL_ASSERT(new_pc == GetCursorOffset());
218
USE(new_pc);
219
}
220
}
221
222
223
void MacroAssembler::HandleOutOfBoundsImmediate(Condition cond,
224
Register tmp,
225
uint32_t imm) {
226
if (IsUintN(16, imm)) {
227
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
228
mov(cond, tmp, imm & 0xffff);
229
return;
230
}
231
if (IsUsingT32()) {
232
if (ImmediateT32::IsImmediateT32(~imm)) {
233
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
234
mvn(cond, tmp, ~imm);
235
return;
236
}
237
} else {
238
if (ImmediateA32::IsImmediateA32(~imm)) {
239
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
240
mvn(cond, tmp, ~imm);
241
return;
242
}
243
}
244
CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
245
mov(cond, tmp, imm & 0xffff);
246
movt(cond, tmp, imm >> 16);
247
}
248
249
250
MemOperand MacroAssembler::MemOperandComputationHelper(
251
Condition cond,
252
Register scratch,
253
Register base,
254
uint32_t offset,
255
uint32_t extra_offset_mask) {
256
VIXL_ASSERT(!AliasesAvailableScratchRegister(scratch));
257
VIXL_ASSERT(!AliasesAvailableScratchRegister(base));
258
VIXL_ASSERT(allow_macro_instructions_);
259
VIXL_ASSERT(OutsideITBlock());
260
261
// Check for the simple pass-through case.
262
if ((offset & extra_offset_mask) == offset) return MemOperand(base, offset);
263
264
MacroEmissionCheckScope guard(this);
265
ITScope it_scope(this, &cond, guard);
266
267
uint32_t load_store_offset = offset & extra_offset_mask;
268
uint32_t add_offset = offset & ~extra_offset_mask;
269
if ((add_offset != 0) && (IsModifiedImmediate(offset) ||
270
IsModifiedImmediate(UnsignedNegate(offset)))) {
271
load_store_offset = 0;
272
add_offset = offset;
273
}
274
275
if (base.IsPC()) {
276
// Special handling for PC bases. We must read the PC in the first
277
// instruction (and only in that instruction), and we must also take care to
278
// keep the same address calculation as loads and stores. For T32, that
279
// means using something like ADR, which uses AlignDown(PC, 4).
280
281
// We don't handle positive offsets from PC because the intention is not
282
// clear; does the user expect the offset from the current
283
// GetCursorOffset(), or to allow a certain amount of space after the
284
// instruction?
285
VIXL_ASSERT((offset & 0x80000000) != 0);
286
if (IsUsingT32()) {
287
// T32: make the first instruction "SUB (immediate, from PC)" -- an alias
288
// of ADR -- to get behaviour like loads and stores. This ADR can handle
289
// at least as much offset as the load_store_offset so it can replace it.
290
291
uint32_t sub_pc_offset = UnsignedNegate(offset) & 0xfff;
292
load_store_offset = (offset + sub_pc_offset) & extra_offset_mask;
293
add_offset = (offset + sub_pc_offset) & ~extra_offset_mask;
294
295
ExactAssemblyScope scope(this, k32BitT32InstructionSizeInBytes);
296
sub(cond, scratch, base, sub_pc_offset);
297
298
if (add_offset == 0) return MemOperand(scratch, load_store_offset);
299
300
// The rest of the offset can be generated in the usual way.
301
base = scratch;
302
}
303
// A32 can use any SUB instruction, so we don't have to do anything special
304
// here except to ensure that we read the PC first.
305
}
306
307
add(cond, scratch, base, add_offset);
308
return MemOperand(scratch, load_store_offset);
309
}
310
311
312
uint32_t MacroAssembler::GetOffsetMask(InstructionType type,
313
AddrMode addrmode) {
314
switch (type) {
315
case kLdr:
316
case kLdrb:
317
case kStr:
318
case kStrb:
319
if (IsUsingA32() || (addrmode == Offset)) {
320
return 0xfff;
321
} else {
322
return 0xff;
323
}
324
case kLdrsb:
325
case kLdrh:
326
case kLdrsh:
327
case kStrh:
328
if (IsUsingT32() && (addrmode == Offset)) {
329
return 0xfff;
330
} else {
331
return 0xff;
332
}
333
case kVldr:
334
case kVstr:
335
return 0x3fc;
336
case kLdrd:
337
case kStrd:
338
if (IsUsingA32()) {
339
return 0xff;
340
} else {
341
return 0x3fc;
342
}
343
default:
344
VIXL_UNREACHABLE();
345
return 0;
346
}
347
}
348
349
350
HARDFLOAT void PrintfTrampolineRRRR(
351
const char* format, uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
352
printf(format, a, b, c, d);
353
}
354
355
356
HARDFLOAT void PrintfTrampolineRRRD(
357
const char* format, uint32_t a, uint32_t b, uint32_t c, double d) {
358
printf(format, a, b, c, d);
359
}
360
361
362
HARDFLOAT void PrintfTrampolineRRDR(
363
const char* format, uint32_t a, uint32_t b, double c, uint32_t d) {
364
printf(format, a, b, c, d);
365
}
366
367
368
HARDFLOAT void PrintfTrampolineRRDD(
369
const char* format, uint32_t a, uint32_t b, double c, double d) {
370
printf(format, a, b, c, d);
371
}
372
373
374
HARDFLOAT void PrintfTrampolineRDRR(
375
const char* format, uint32_t a, double b, uint32_t c, uint32_t d) {
376
printf(format, a, b, c, d);
377
}
378
379
380
HARDFLOAT void PrintfTrampolineRDRD(
381
const char* format, uint32_t a, double b, uint32_t c, double d) {
382
printf(format, a, b, c, d);
383
}
384
385
386
HARDFLOAT void PrintfTrampolineRDDR(
387
const char* format, uint32_t a, double b, double c, uint32_t d) {
388
printf(format, a, b, c, d);
389
}
390
391
392
HARDFLOAT void PrintfTrampolineRDDD(
393
const char* format, uint32_t a, double b, double c, double d) {
394
printf(format, a, b, c, d);
395
}
396
397
398
HARDFLOAT void PrintfTrampolineDRRR(
399
const char* format, double a, uint32_t b, uint32_t c, uint32_t d) {
400
printf(format, a, b, c, d);
401
}
402
403
404
HARDFLOAT void PrintfTrampolineDRRD(
405
const char* format, double a, uint32_t b, uint32_t c, double d) {
406
printf(format, a, b, c, d);
407
}
408
409
410
HARDFLOAT void PrintfTrampolineDRDR(
411
const char* format, double a, uint32_t b, double c, uint32_t d) {
412
printf(format, a, b, c, d);
413
}
414
415
416
HARDFLOAT void PrintfTrampolineDRDD(
417
const char* format, double a, uint32_t b, double c, double d) {
418
printf(format, a, b, c, d);
419
}
420
421
422
HARDFLOAT void PrintfTrampolineDDRR(
423
const char* format, double a, double b, uint32_t c, uint32_t d) {
424
printf(format, a, b, c, d);
425
}
426
427
428
HARDFLOAT void PrintfTrampolineDDRD(
429
const char* format, double a, double b, uint32_t c, double d) {
430
printf(format, a, b, c, d);
431
}
432
433
434
HARDFLOAT void PrintfTrampolineDDDR(
435
const char* format, double a, double b, double c, uint32_t d) {
436
printf(format, a, b, c, d);
437
}
438
439
440
HARDFLOAT void PrintfTrampolineDDDD(
441
const char* format, double a, double b, double c, double d) {
442
printf(format, a, b, c, d);
443
}
444
445
446
void MacroAssembler::Printf(const char* format,
447
CPURegister reg1,
448
CPURegister reg2,
449
CPURegister reg3,
450
CPURegister reg4) {
451
// Exclude all registers from the available scratch registers, so
452
// that we are able to use ip below.
453
// TODO: Refactor this function to use UseScratchRegisterScope
454
// for temporary registers below.
455
UseScratchRegisterScope scratch(this);
456
scratch.ExcludeAll();
457
if (generate_simulator_code_) {
458
PushRegister(reg4);
459
PushRegister(reg3);
460
PushRegister(reg2);
461
PushRegister(reg1);
462
Push(RegisterList(r0, r1));
463
StringLiteral* format_literal =
464
new StringLiteral(format, RawLiteral::kDeletedOnPlacementByPool);
465
Adr(r0, format_literal);
466
uint32_t args = (reg4.GetType() << 12) | (reg3.GetType() << 8) |
467
(reg2.GetType() << 4) | reg1.GetType();
468
Mov(r1, args);
469
Hvc(kPrintfCode);
470
Pop(RegisterList(r0, r1));
471
int size = reg4.GetRegSizeInBytes() + reg3.GetRegSizeInBytes() +
472
reg2.GetRegSizeInBytes() + reg1.GetRegSizeInBytes();
473
Drop(size);
474
} else {
475
// Generate on a native platform => 32 bit environment.
476
// Preserve core registers r0-r3, r12, r14
477
const uint32_t saved_registers_mask =
478
kCallerSavedRegistersMask | (1 << r5.GetCode());
479
Push(RegisterList(saved_registers_mask));
480
// Push VFP registers.
481
Vpush(Untyped64, DRegisterList(d0, 8));
482
if (Has32DRegs()) Vpush(Untyped64, DRegisterList(d16, 16));
483
// Search one register which has been saved and which doesn't need to be
484
// printed.
485
RegisterList available_registers(kCallerSavedRegistersMask);
486
if (reg1.GetType() == CPURegister::kRRegister) {
487
available_registers.Remove(Register(reg1.GetCode()));
488
}
489
if (reg2.GetType() == CPURegister::kRRegister) {
490
available_registers.Remove(Register(reg2.GetCode()));
491
}
492
if (reg3.GetType() == CPURegister::kRRegister) {
493
available_registers.Remove(Register(reg3.GetCode()));
494
}
495
if (reg4.GetType() == CPURegister::kRRegister) {
496
available_registers.Remove(Register(reg4.GetCode()));
497
}
498
Register tmp = available_registers.GetFirstAvailableRegister();
499
VIXL_ASSERT(tmp.GetType() == CPURegister::kRRegister);
500
// Push the flags.
501
Mrs(tmp, APSR);
502
Push(tmp);
503
Vmrs(RegisterOrAPSR_nzcv(tmp.GetCode()), FPSCR);
504
Push(tmp);
505
// Push the registers to print on the stack.
506
PushRegister(reg4);
507
PushRegister(reg3);
508
PushRegister(reg2);
509
PushRegister(reg1);
510
int core_count = 1;
511
int vfp_count = 0;
512
uint32_t printf_type = 0;
513
// Pop the registers to print and store them into r1-r3 and/or d0-d3.
514
// Reg4 may stay into the stack if all the register to print are core
515
// registers.
516
PreparePrintfArgument(reg1, &core_count, &vfp_count, &printf_type);
517
PreparePrintfArgument(reg2, &core_count, &vfp_count, &printf_type);
518
PreparePrintfArgument(reg3, &core_count, &vfp_count, &printf_type);
519
PreparePrintfArgument(reg4, &core_count, &vfp_count, &printf_type);
520
// Ensure that the stack is aligned on 8 bytes.
521
And(r5, sp, 0x7);
522
if (core_count == 5) {
523
// One 32 bit argument (reg4) has been left on the stack => align the
524
// stack
525
// before the argument.
526
Pop(r0);
527
Sub(sp, sp, r5);
528
Push(r0);
529
} else {
530
Sub(sp, sp, r5);
531
}
532
// Select the right trampoline depending on the arguments.
533
uintptr_t address;
534
switch (printf_type) {
535
case 0:
536
address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRRR);
537
break;
538
case 1:
539
address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRRR);
540
break;
541
case 2:
542
address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDRR);
543
break;
544
case 3:
545
address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDRR);
546
break;
547
case 4:
548
address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRDR);
549
break;
550
case 5:
551
address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRDR);
552
break;
553
case 6:
554
address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDDR);
555
break;
556
case 7:
557
address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDDR);
558
break;
559
case 8:
560
address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRRD);
561
break;
562
case 9:
563
address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRRD);
564
break;
565
case 10:
566
address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDRD);
567
break;
568
case 11:
569
address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDRD);
570
break;
571
case 12:
572
address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRDD);
573
break;
574
case 13:
575
address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRDD);
576
break;
577
case 14:
578
address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDDD);
579
break;
580
case 15:
581
address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDDD);
582
break;
583
default:
584
VIXL_UNREACHABLE();
585
address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRRR);
586
break;
587
}
588
StringLiteral* format_literal =
589
new StringLiteral(format, RawLiteral::kDeletedOnPlacementByPool);
590
Adr(r0, format_literal);
591
Mov(ip, Operand::From(address));
592
Blx(ip);
593
// If register reg4 was left on the stack => skip it.
594
if (core_count == 5) Drop(kRegSizeInBytes);
595
// Restore the stack as it was before alignment.
596
Add(sp, sp, r5);
597
// Restore the flags.
598
Pop(tmp);
599
Vmsr(FPSCR, tmp);
600
Pop(tmp);
601
Msr(APSR_nzcvqg, tmp);
602
// Restore the registers.
603
if (Has32DRegs()) Vpop(Untyped64, DRegisterList(d16, 16));
604
Vpop(Untyped64, DRegisterList(d0, 8));
605
Pop(RegisterList(saved_registers_mask));
606
}
607
}
608
609
610
void MacroAssembler::PushRegister(CPURegister reg) {
611
switch (reg.GetType()) {
612
case CPURegister::kNoRegister:
613
break;
614
case CPURegister::kRRegister:
615
Push(Register(reg.GetCode()));
616
break;
617
case CPURegister::kSRegister:
618
Vpush(Untyped32, SRegisterList(SRegister(reg.GetCode())));
619
break;
620
case CPURegister::kDRegister:
621
Vpush(Untyped64, DRegisterList(DRegister(reg.GetCode())));
622
break;
623
case CPURegister::kQRegister:
624
VIXL_UNIMPLEMENTED();
625
break;
626
}
627
}
628
629
630
void MacroAssembler::PreparePrintfArgument(CPURegister reg,
631
int* core_count,
632
int* vfp_count,
633
uint32_t* printf_type) {
634
switch (reg.GetType()) {
635
case CPURegister::kNoRegister:
636
break;
637
case CPURegister::kRRegister:
638
VIXL_ASSERT(*core_count <= 4);
639
if (*core_count < 4) Pop(Register(*core_count));
640
*core_count += 1;
641
break;
642
case CPURegister::kSRegister:
643
VIXL_ASSERT(*vfp_count < 4);
644
*printf_type |= 1 << (*core_count + *vfp_count - 1);
645
Vpop(Untyped32, SRegisterList(SRegister(*vfp_count * 2)));
646
Vcvt(F64, F32, DRegister(*vfp_count), SRegister(*vfp_count * 2));
647
*vfp_count += 1;
648
break;
649
case CPURegister::kDRegister:
650
VIXL_ASSERT(*vfp_count < 4);
651
*printf_type |= 1 << (*core_count + *vfp_count - 1);
652
Vpop(Untyped64, DRegisterList(DRegister(*vfp_count)));
653
*vfp_count += 1;
654
break;
655
case CPURegister::kQRegister:
656
VIXL_UNIMPLEMENTED();
657
break;
658
}
659
}
660
661
662
void MacroAssembler::Delegate(InstructionType type,
663
InstructionCondROp instruction,
664
Condition cond,
665
Register rn,
666
const Operand& operand) {
667
VIXL_ASSERT((type == kMovt) || (type == kSxtb16) || (type == kTeq) ||
668
(type == kUxtb16));
669
670
if (type == kMovt) {
671
VIXL_ABORT_WITH_MSG("`Movt` expects a 16-bit immediate.\n");
672
}
673
674
// This delegate only supports teq with immediates.
675
CONTEXT_SCOPE;
676
if ((type == kTeq) && operand.IsImmediate()) {
677
UseScratchRegisterScope temps(this);
678
Register scratch = temps.Acquire();
679
HandleOutOfBoundsImmediate(cond, scratch, operand.GetImmediate());
680
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
681
teq(cond, rn, scratch);
682
return;
683
}
684
Assembler::Delegate(type, instruction, cond, rn, operand);
685
}
686
687
688
void MacroAssembler::Delegate(InstructionType type,
689
InstructionCondSizeROp instruction,
690
Condition cond,
691
EncodingSize size,
692
Register rn,
693
const Operand& operand) {
694
CONTEXT_SCOPE;
695
VIXL_ASSERT(size.IsBest());
696
VIXL_ASSERT((type == kCmn) || (type == kCmp) || (type == kMov) ||
697
(type == kMovs) || (type == kMvn) || (type == kMvns) ||
698
(type == kSxtb) || (type == kSxth) || (type == kTst) ||
699
(type == kUxtb) || (type == kUxth));
700
if (IsUsingT32() && operand.IsRegisterShiftedRegister()) {
701
VIXL_ASSERT((type != kMov) || (type != kMovs));
702
InstructionCondRROp shiftop = NULL;
703
switch (operand.GetShift().GetType()) {
704
case LSL:
705
shiftop = &Assembler::lsl;
706
break;
707
case LSR:
708
shiftop = &Assembler::lsr;
709
break;
710
case ASR:
711
shiftop = &Assembler::asr;
712
break;
713
case RRX:
714
// A RegisterShiftedRegister operand cannot have a shift of type RRX.
715
VIXL_UNREACHABLE();
716
break;
717
case ROR:
718
shiftop = &Assembler::ror;
719
break;
720
default:
721
VIXL_UNREACHABLE();
722
}
723
if (shiftop != NULL) {
724
UseScratchRegisterScope temps(this);
725
Register scratch = temps.Acquire();
726
CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
727
(this->*shiftop)(cond,
728
scratch,
729
operand.GetBaseRegister(),
730
operand.GetShiftRegister());
731
(this->*instruction)(cond, size, rn, scratch);
732
return;
733
}
734
}
735
if (operand.IsImmediate()) {
736
uint32_t imm = operand.GetImmediate();
737
switch (type) {
738
case kMov:
739
case kMovs:
740
if (!rn.IsPC()) {
741
// Immediate is too large, but not using PC, so handle with mov{t}.
742
HandleOutOfBoundsImmediate(cond, rn, imm);
743
if (type == kMovs) {
744
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
745
tst(cond, rn, rn);
746
}
747
return;
748
} else if (type == kMov) {
749
VIXL_ASSERT(IsUsingA32() || cond.Is(al));
750
// Immediate is too large and using PC, so handle using a temporary
751
// register.
752
UseScratchRegisterScope temps(this);
753
Register scratch = temps.Acquire();
754
HandleOutOfBoundsImmediate(al, scratch, imm);
755
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
756
bx(cond, scratch);
757
return;
758
}
759
break;
760
case kCmn:
761
case kCmp:
762
if (IsUsingA32() || !rn.IsPC()) {
763
UseScratchRegisterScope temps(this);
764
Register scratch = temps.Acquire();
765
HandleOutOfBoundsImmediate(cond, scratch, imm);
766
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
767
(this->*instruction)(cond, size, rn, scratch);
768
return;
769
}
770
break;
771
case kMvn:
772
case kMvns:
773
if (!rn.IsPC()) {
774
UseScratchRegisterScope temps(this);
775
Register scratch = temps.Acquire();
776
HandleOutOfBoundsImmediate(cond, scratch, imm);
777
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
778
(this->*instruction)(cond, size, rn, scratch);
779
return;
780
}
781
break;
782
case kTst:
783
if (IsUsingA32() || !rn.IsPC()) {
784
UseScratchRegisterScope temps(this);
785
Register scratch = temps.Acquire();
786
HandleOutOfBoundsImmediate(cond, scratch, imm);
787
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
788
(this->*instruction)(cond, size, rn, scratch);
789
return;
790
}
791
break;
792
default: // kSxtb, Sxth, Uxtb, Uxth
793
break;
794
}
795
}
796
Assembler::Delegate(type, instruction, cond, size, rn, operand);
797
}
798
799
800
void MacroAssembler::Delegate(InstructionType type,
801
InstructionCondRROp instruction,
802
Condition cond,
803
Register rd,
804
Register rn,
805
const Operand& operand) {
806
if ((type == kSxtab) || (type == kSxtab16) || (type == kSxtah) ||
807
(type == kUxtab) || (type == kUxtab16) || (type == kUxtah) ||
808
(type == kPkhbt) || (type == kPkhtb)) {
809
UnimplementedDelegate(type);
810
return;
811
}
812
813
// This delegate only handles the following instructions.
814
VIXL_ASSERT((type == kOrn) || (type == kOrns) || (type == kRsc) ||
815
(type == kRscs));
816
CONTEXT_SCOPE;
817
818
// T32 does not support register shifted register operands, emulate it.
819
if (IsUsingT32() && operand.IsRegisterShiftedRegister()) {
820
InstructionCondRROp shiftop = NULL;
821
switch (operand.GetShift().GetType()) {
822
case LSL:
823
shiftop = &Assembler::lsl;
824
break;
825
case LSR:
826
shiftop = &Assembler::lsr;
827
break;
828
case ASR:
829
shiftop = &Assembler::asr;
830
break;
831
case RRX:
832
// A RegisterShiftedRegister operand cannot have a shift of type RRX.
833
VIXL_UNREACHABLE();
834
break;
835
case ROR:
836
shiftop = &Assembler::ror;
837
break;
838
default:
839
VIXL_UNREACHABLE();
840
}
841
if (shiftop != NULL) {
842
UseScratchRegisterScope temps(this);
843
Register rm = operand.GetBaseRegister();
844
Register rs = operand.GetShiftRegister();
845
// Try to use rd as a scratch register. We can do this if it aliases rs or
846
// rm (because we read them in the first instruction), but not rn.
847
if (!rd.Is(rn)) temps.Include(rd);
848
Register scratch = temps.Acquire();
849
// TODO: The scope length was measured empirically. We should analyse the
850
// worst-case size and add targetted tests.
851
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
852
(this->*shiftop)(cond, scratch, rm, rs);
853
(this->*instruction)(cond, rd, rn, scratch);
854
return;
855
}
856
}
857
858
// T32 does not have a Rsc instruction, negate the lhs input and turn it into
859
// an Adc. Adc and Rsc are equivalent using a bitwise NOT:
860
// adc rd, rn, operand <-> rsc rd, NOT(rn), operand
861
if (IsUsingT32() && ((type == kRsc) || (type == kRscs))) {
862
// The RegisterShiftRegister case should have been handled above.
863
VIXL_ASSERT(!operand.IsRegisterShiftedRegister());
864
UseScratchRegisterScope temps(this);
865
// Try to use rd as a scratch register. We can do this if it aliases rn
866
// (because we read it in the first instruction), but not rm.
867
temps.Include(rd);
868
temps.Exclude(operand);
869
Register negated_rn = temps.Acquire();
870
{
871
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
872
mvn(cond, negated_rn, rn);
873
}
874
if (type == kRsc) {
875
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
876
adc(cond, rd, negated_rn, operand);
877
return;
878
}
879
// TODO: We shouldn't have to specify how much space the next instruction
880
// needs.
881
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
882
adcs(cond, rd, negated_rn, operand);
883
return;
884
}
885
886
if (operand.IsImmediate()) {
887
// If the immediate can be encoded when inverted, turn Orn into Orr.
888
// Otherwise rely on HandleOutOfBoundsImmediate to generate a series of
889
// mov.
890
int32_t imm = operand.GetSignedImmediate();
891
if (((type == kOrn) || (type == kOrns)) && IsModifiedImmediate(~imm)) {
892
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
893
switch (type) {
894
case kOrn:
895
orr(cond, rd, rn, ~imm);
896
return;
897
case kOrns:
898
orrs(cond, rd, rn, ~imm);
899
return;
900
default:
901
VIXL_UNREACHABLE();
902
break;
903
}
904
}
905
}
906
907
// A32 does not have a Orn instruction, negate the rhs input and turn it into
908
// a Orr.
909
if (IsUsingA32() && ((type == kOrn) || (type == kOrns))) {
910
// TODO: orn r0, r1, imm -> orr r0, r1, neg(imm) if doable
911
// mvn r0, r2
912
// orr r0, r1, r0
913
Register scratch;
914
UseScratchRegisterScope temps(this);
915
// Try to use rd as a scratch register. We can do this if it aliases rs or
916
// rm (because we read them in the first instruction), but not rn.
917
if (!rd.Is(rn)) temps.Include(rd);
918
scratch = temps.Acquire();
919
{
920
// TODO: We shouldn't have to specify how much space the next instruction
921
// needs.
922
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
923
mvn(cond, scratch, operand);
924
}
925
if (type == kOrns) {
926
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
927
orrs(cond, rd, rn, scratch);
928
return;
929
}
930
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
931
orr(cond, rd, rn, scratch);
932
return;
933
}
934
935
if (operand.IsImmediate()) {
936
UseScratchRegisterScope temps(this);
937
// Allow using the destination as a scratch register if possible.
938
if (!rd.Is(rn)) temps.Include(rd);
939
Register scratch = temps.Acquire();
940
int32_t imm = operand.GetSignedImmediate();
941
HandleOutOfBoundsImmediate(cond, scratch, imm);
942
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
943
(this->*instruction)(cond, rd, rn, scratch);
944
return;
945
}
946
Assembler::Delegate(type, instruction, cond, rd, rn, operand);
947
}
948
949
950
void MacroAssembler::Delegate(InstructionType type,
951
InstructionCondSizeRL instruction,
952
Condition cond,
953
EncodingSize size,
954
Register rd,
955
Location* location) {
956
VIXL_ASSERT((type == kLdr) || (type == kAdr));
957
958
CONTEXT_SCOPE;
959
VIXL_ASSERT(size.IsBest());
960
961
if ((type == kLdr) && location->IsBound()) {
962
CodeBufferCheckScope scope(this, 5 * kMaxInstructionSizeInBytes);
963
UseScratchRegisterScope temps(this);
964
temps.Include(rd);
965
uint32_t mask = GetOffsetMask(type, Offset);
966
ldr(rd, MemOperandComputationHelper(cond, temps.Acquire(), location, mask));
967
return;
968
}
969
970
Assembler::Delegate(type, instruction, cond, size, rd, location);
971
}
972
973
974
bool MacroAssembler::GenerateSplitInstruction(
975
InstructionCondSizeRROp instruction,
976
Condition cond,
977
Register rd,
978
Register rn,
979
uint32_t imm,
980
uint32_t mask) {
981
uint32_t high = imm & ~mask;
982
if (!IsModifiedImmediate(high) && !rn.IsPC()) return false;
983
// If high is a modified immediate, we can perform the operation with
984
// only 2 instructions.
985
// Else, if rn is PC, we want to avoid moving PC into a temporary.
986
// Therefore, we also use the pattern even if the second call may
987
// generate 3 instructions.
988
uint32_t low = imm & mask;
989
CodeBufferCheckScope scope(this,
990
(rn.IsPC() ? 4 : 2) * kMaxInstructionSizeInBytes);
991
(this->*instruction)(cond, Best, rd, rn, low);
992
(this->*instruction)(cond, Best, rd, rd, high);
993
return true;
994
}
995
996
997
void MacroAssembler::Delegate(InstructionType type,
998
InstructionCondSizeRROp instruction,
999
Condition cond,
1000
EncodingSize size,
1001
Register rd,
1002
Register rn,
1003
const Operand& operand) {
1004
VIXL_ASSERT(
1005
(type == kAdc) || (type == kAdcs) || (type == kAdd) || (type == kAdds) ||
1006
(type == kAnd) || (type == kAnds) || (type == kAsr) || (type == kAsrs) ||
1007
(type == kBic) || (type == kBics) || (type == kEor) || (type == kEors) ||
1008
(type == kLsl) || (type == kLsls) || (type == kLsr) || (type == kLsrs) ||
1009
(type == kOrr) || (type == kOrrs) || (type == kRor) || (type == kRors) ||
1010
(type == kRsb) || (type == kRsbs) || (type == kSbc) || (type == kSbcs) ||
1011
(type == kSub) || (type == kSubs));
1012
1013
CONTEXT_SCOPE;
1014
VIXL_ASSERT(size.IsBest());
1015
if (IsUsingT32() && operand.IsRegisterShiftedRegister()) {
1016
InstructionCondRROp shiftop = NULL;
1017
switch (operand.GetShift().GetType()) {
1018
case LSL:
1019
shiftop = &Assembler::lsl;
1020
break;
1021
case LSR:
1022
shiftop = &Assembler::lsr;
1023
break;
1024
case ASR:
1025
shiftop = &Assembler::asr;
1026
break;
1027
case RRX:
1028
// A RegisterShiftedRegister operand cannot have a shift of type RRX.
1029
VIXL_UNREACHABLE();
1030
break;
1031
case ROR:
1032
shiftop = &Assembler::ror;
1033
break;
1034
default:
1035
VIXL_UNREACHABLE();
1036
}
1037
if (shiftop != NULL) {
1038
UseScratchRegisterScope temps(this);
1039
Register rm = operand.GetBaseRegister();
1040
Register rs = operand.GetShiftRegister();
1041
// Try to use rd as a scratch register. We can do this if it aliases rs or
1042
// rm (because we read them in the first instruction), but not rn.
1043
if (!rd.Is(rn)) temps.Include(rd);
1044
Register scratch = temps.Acquire();
1045
CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
1046
(this->*shiftop)(cond, scratch, rm, rs);
1047
(this->*instruction)(cond, size, rd, rn, scratch);
1048
return;
1049
}
1050
}
1051
if (operand.IsImmediate()) {
1052
int32_t imm = operand.GetSignedImmediate();
1053
if (ImmediateT32::IsImmediateT32(~imm)) {
1054
if (IsUsingT32()) {
1055
switch (type) {
1056
case kOrr:
1057
orn(cond, rd, rn, ~imm);
1058
return;
1059
case kOrrs:
1060
orns(cond, rd, rn, ~imm);
1061
return;
1062
default:
1063
break;
1064
}
1065
}
1066
}
1067
if (imm < 0) {
1068
InstructionCondSizeRROp asmcb = NULL;
1069
// Add and sub are equivalent using an arithmetic negation:
1070
// add rd, rn, #imm <-> sub rd, rn, - #imm
1071
// Add and sub with carry are equivalent using a bitwise NOT:
1072
// adc rd, rn, #imm <-> sbc rd, rn, NOT #imm
1073
switch (type) {
1074
case kAdd:
1075
asmcb = &Assembler::sub;
1076
imm = -imm;
1077
break;
1078
case kAdds:
1079
asmcb = &Assembler::subs;
1080
imm = -imm;
1081
break;
1082
case kSub:
1083
asmcb = &Assembler::add;
1084
imm = -imm;
1085
break;
1086
case kSubs:
1087
asmcb = &Assembler::adds;
1088
imm = -imm;
1089
break;
1090
case kAdc:
1091
asmcb = &Assembler::sbc;
1092
imm = ~imm;
1093
break;
1094
case kAdcs:
1095
asmcb = &Assembler::sbcs;
1096
imm = ~imm;
1097
break;
1098
case kSbc:
1099
asmcb = &Assembler::adc;
1100
imm = ~imm;
1101
break;
1102
case kSbcs:
1103
asmcb = &Assembler::adcs;
1104
imm = ~imm;
1105
break;
1106
default:
1107
break;
1108
}
1109
if (asmcb != NULL) {
1110
CodeBufferCheckScope scope(this, 4 * kMaxInstructionSizeInBytes);
1111
(this->*asmcb)(cond, size, rd, rn, Operand(imm));
1112
return;
1113
}
1114
}
1115
1116
// When rn is PC, only handle negative offsets. The correct way to handle
1117
// positive offsets isn't clear; does the user want the offset from the
1118
// start of the macro, or from the end (to allow a certain amount of space)?
1119
// When type is Add or Sub, imm is always positive (imm < 0 has just been
1120
// handled and imm == 0 would have been generated without the need of a
1121
// delegate). Therefore, only add to PC is forbidden here.
1122
if ((((type == kAdd) && !rn.IsPC()) || (type == kSub)) &&
1123
(IsUsingA32() || (!rd.IsPC() && !rn.IsPC()))) {
1124
VIXL_ASSERT(imm > 0);
1125
// Try to break the constant into two modified immediates.
1126
// For T32 also try to break the constant into one imm12 and one modified
1127
// immediate. Count the trailing zeroes and get the biggest even value.
1128
int trailing_zeroes = CountTrailingZeros(imm) & ~1u;
1129
uint32_t mask = ((trailing_zeroes < 4) && IsUsingT32())
1130
? 0xfff
1131
: (0xff << trailing_zeroes);
1132
if (GenerateSplitInstruction(instruction, cond, rd, rn, imm, mask)) {
1133
return;
1134
}
1135
InstructionCondSizeRROp asmcb = NULL;
1136
switch (type) {
1137
case kAdd:
1138
asmcb = &Assembler::sub;
1139
break;
1140
case kSub:
1141
asmcb = &Assembler::add;
1142
break;
1143
default:
1144
VIXL_UNREACHABLE();
1145
}
1146
if (GenerateSplitInstruction(asmcb, cond, rd, rn, -imm, mask)) {
1147
return;
1148
}
1149
}
1150
1151
UseScratchRegisterScope temps(this);
1152
// Allow using the destination as a scratch register if possible.
1153
if (!rd.Is(rn)) temps.Include(rd);
1154
if (rn.IsPC()) {
1155
// If we're reading the PC, we need to do it in the first instruction,
1156
// otherwise we'll read the wrong value. We rely on this to handle the
1157
// long-range PC-relative MemOperands which can result from user-managed
1158
// literals.
1159
1160
// Only handle negative offsets. The correct way to handle positive
1161
// offsets isn't clear; does the user want the offset from the start of
1162
// the macro, or from the end (to allow a certain amount of space)?
1163
bool offset_is_negative_or_zero = (imm <= 0);
1164
switch (type) {
1165
case kAdd:
1166
case kAdds:
1167
offset_is_negative_or_zero = (imm <= 0);
1168
break;
1169
case kSub:
1170
case kSubs:
1171
offset_is_negative_or_zero = (imm >= 0);
1172
break;
1173
case kAdc:
1174
case kAdcs:
1175
offset_is_negative_or_zero = (imm < 0);
1176
break;
1177
case kSbc:
1178
case kSbcs:
1179
offset_is_negative_or_zero = (imm > 0);
1180
break;
1181
default:
1182
break;
1183
}
1184
if (offset_is_negative_or_zero) {
1185
{
1186
rn = temps.Acquire();
1187
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1188
mov(cond, rn, pc);
1189
}
1190
// Recurse rather than falling through, to try to get the immediate into
1191
// a single instruction.
1192
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
1193
(this->*instruction)(cond, size, rd, rn, operand);
1194
return;
1195
}
1196
} else {
1197
Register scratch = temps.Acquire();
1198
// TODO: The scope length was measured empirically. We should analyse the
1199
// worst-case size and add targetted tests.
1200
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
1201
mov(cond, scratch, operand.GetImmediate());
1202
(this->*instruction)(cond, size, rd, rn, scratch);
1203
return;
1204
}
1205
}
1206
Assembler::Delegate(type, instruction, cond, size, rd, rn, operand);
1207
}
1208
1209
1210
void MacroAssembler::Delegate(InstructionType type,
1211
InstructionRL instruction,
1212
Register rn,
1213
Location* location) {
1214
VIXL_ASSERT((type == kCbz) || (type == kCbnz));
1215
1216
CONTEXT_SCOPE;
1217
CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
1218
if (IsUsingA32()) {
1219
if (type == kCbz) {
1220
VIXL_ABORT_WITH_MSG("Cbz is only available for T32.\n");
1221
} else {
1222
VIXL_ABORT_WITH_MSG("Cbnz is only available for T32.\n");
1223
}
1224
} else if (rn.IsLow()) {
1225
switch (type) {
1226
case kCbnz: {
1227
Label done;
1228
cbz(rn, &done);
1229
b(location);
1230
Bind(&done);
1231
return;
1232
}
1233
case kCbz: {
1234
Label done;
1235
cbnz(rn, &done);
1236
b(location);
1237
Bind(&done);
1238
return;
1239
}
1240
default:
1241
break;
1242
}
1243
}
1244
Assembler::Delegate(type, instruction, rn, location);
1245
}
1246
1247
1248
template <typename T>
1249
static inline bool IsI64BitPattern(T imm) {
1250
for (T mask = 0xff << ((sizeof(T) - 1) * 8); mask != 0; mask >>= 8) {
1251
if (((imm & mask) != mask) && ((imm & mask) != 0)) return false;
1252
}
1253
return true;
1254
}
1255
1256
1257
template <typename T>
1258
static inline bool IsI8BitPattern(T imm) {
1259
uint8_t imm8 = imm & 0xff;
1260
for (unsigned rep = sizeof(T) - 1; rep > 0; rep--) {
1261
imm >>= 8;
1262
if ((imm & 0xff) != imm8) return false;
1263
}
1264
return true;
1265
}
1266
1267
1268
static inline bool CanBeInverted(uint32_t imm32) {
1269
uint32_t fill8 = 0;
1270
1271
if ((imm32 & 0xffffff00) == 0xffffff00) {
1272
// 11111111 11111111 11111111 abcdefgh
1273
return true;
1274
}
1275
if (((imm32 & 0xff) == 0) || ((imm32 & 0xff) == 0xff)) {
1276
fill8 = imm32 & 0xff;
1277
imm32 >>= 8;
1278
if ((imm32 >> 8) == 0xffff) {
1279
// 11111111 11111111 abcdefgh 00000000
1280
// or 11111111 11111111 abcdefgh 11111111
1281
return true;
1282
}
1283
if ((imm32 & 0xff) == fill8) {
1284
imm32 >>= 8;
1285
if ((imm32 >> 8) == 0xff) {
1286
// 11111111 abcdefgh 00000000 00000000
1287
// or 11111111 abcdefgh 11111111 11111111
1288
return true;
1289
}
1290
if ((fill8 == 0xff) && ((imm32 & 0xff) == 0xff)) {
1291
// abcdefgh 11111111 11111111 11111111
1292
return true;
1293
}
1294
}
1295
}
1296
return false;
1297
}
1298
1299
1300
template <typename RES, typename T>
1301
static inline RES replicate(T imm) {
1302
VIXL_ASSERT((sizeof(RES) > sizeof(T)) &&
1303
(((sizeof(RES) / sizeof(T)) * sizeof(T)) == sizeof(RES)));
1304
RES res = imm;
1305
for (unsigned i = sizeof(RES) / sizeof(T) - 1; i > 0; i--) {
1306
res = (res << (sizeof(T) * 8)) | imm;
1307
}
1308
return res;
1309
}
1310
1311
1312
void MacroAssembler::Delegate(InstructionType type,
1313
InstructionCondDtSSop instruction,
1314
Condition cond,
1315
DataType dt,
1316
SRegister rd,
1317
const SOperand& operand) {
1318
CONTEXT_SCOPE;
1319
if (type == kVmov) {
1320
if (operand.IsImmediate() && dt.Is(F32)) {
1321
const NeonImmediate& neon_imm = operand.GetNeonImmediate();
1322
if (neon_imm.CanConvert<float>()) {
1323
// movw ip, imm16
1324
// movk ip, imm16
1325
// vmov s0, ip
1326
UseScratchRegisterScope temps(this);
1327
Register scratch = temps.Acquire();
1328
float f = neon_imm.GetImmediate<float>();
1329
// TODO: The scope length was measured empirically. We should analyse
1330
// the
1331
// worst-case size and add targetted tests.
1332
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
1333
mov(cond, scratch, FloatToRawbits(f));
1334
vmov(cond, rd, scratch);
1335
return;
1336
}
1337
}
1338
}
1339
Assembler::Delegate(type, instruction, cond, dt, rd, operand);
1340
}
1341
1342
1343
void MacroAssembler::Delegate(InstructionType type,
1344
InstructionCondDtDDop instruction,
1345
Condition cond,
1346
DataType dt,
1347
DRegister rd,
1348
const DOperand& operand) {
1349
CONTEXT_SCOPE;
1350
if (type == kVmov) {
1351
if (operand.IsImmediate()) {
1352
const NeonImmediate& neon_imm = operand.GetNeonImmediate();
1353
switch (dt.GetValue()) {
1354
case I32:
1355
if (neon_imm.CanConvert<uint32_t>()) {
1356
uint32_t imm = neon_imm.GetImmediate<uint32_t>();
1357
// vmov.i32 d0, 0xabababab will translate into vmov.i8 d0, 0xab
1358
if (IsI8BitPattern(imm)) {
1359
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1360
vmov(cond, I8, rd, imm & 0xff);
1361
return;
1362
}
1363
// vmov.i32 d0, 0xff0000ff will translate into
1364
// vmov.i64 d0, 0xff0000ffff0000ff
1365
if (IsI64BitPattern(imm)) {
1366
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1367
vmov(cond, I64, rd, replicate<uint64_t>(imm));
1368
return;
1369
}
1370
// vmov.i32 d0, 0xffab0000 will translate into
1371
// vmvn.i32 d0, 0x0054ffff
1372
if (cond.Is(al) && CanBeInverted(imm)) {
1373
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1374
vmvn(I32, rd, ~imm);
1375
return;
1376
}
1377
}
1378
break;
1379
case I16:
1380
if (neon_imm.CanConvert<uint16_t>()) {
1381
uint16_t imm = neon_imm.GetImmediate<uint16_t>();
1382
// vmov.i16 d0, 0xabab will translate into vmov.i8 d0, 0xab
1383
if (IsI8BitPattern(imm)) {
1384
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1385
vmov(cond, I8, rd, imm & 0xff);
1386
return;
1387
}
1388
}
1389
break;
1390
case I64:
1391
if (neon_imm.CanConvert<uint64_t>()) {
1392
uint64_t imm = neon_imm.GetImmediate<uint64_t>();
1393
// vmov.i64 d0, -1 will translate into vmov.i8 d0, 0xff
1394
if (IsI8BitPattern(imm)) {
1395
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1396
vmov(cond, I8, rd, imm & 0xff);
1397
return;
1398
}
1399
// mov ip, lo(imm64)
1400
// vdup d0, ip
1401
// vdup is prefered to 'vmov d0[0]' as d0[1] does not need to be
1402
// preserved
1403
{
1404
UseScratchRegisterScope temps(this);
1405
Register scratch = temps.Acquire();
1406
{
1407
// TODO: The scope length was measured empirically. We should
1408
// analyse the
1409
// worst-case size and add targetted tests.
1410
CodeBufferCheckScope scope(this,
1411
2 * kMaxInstructionSizeInBytes);
1412
mov(cond, scratch, static_cast<uint32_t>(imm & 0xffffffff));
1413
}
1414
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1415
vdup(cond, Untyped32, rd, scratch);
1416
}
1417
// mov ip, hi(imm64)
1418
// vmov d0[1], ip
1419
{
1420
UseScratchRegisterScope temps(this);
1421
Register scratch = temps.Acquire();
1422
{
1423
// TODO: The scope length was measured empirically. We should
1424
// analyse the
1425
// worst-case size and add targetted tests.
1426
CodeBufferCheckScope scope(this,
1427
2 * kMaxInstructionSizeInBytes);
1428
mov(cond, scratch, static_cast<uint32_t>(imm >> 32));
1429
}
1430
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1431
vmov(cond, Untyped32, DRegisterLane(rd, 1), scratch);
1432
}
1433
return;
1434
}
1435
break;
1436
default:
1437
break;
1438
}
1439
VIXL_ASSERT(!dt.Is(I8)); // I8 cases should have been handled already.
1440
if ((dt.Is(I16) || dt.Is(I32)) && neon_imm.CanConvert<uint32_t>()) {
1441
// mov ip, imm32
1442
// vdup.16 d0, ip
1443
UseScratchRegisterScope temps(this);
1444
Register scratch = temps.Acquire();
1445
{
1446
CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
1447
mov(cond, scratch, neon_imm.GetImmediate<uint32_t>());
1448
}
1449
DataTypeValue vdup_dt = Untyped32;
1450
switch (dt.GetValue()) {
1451
case I16:
1452
vdup_dt = Untyped16;
1453
break;
1454
case I32:
1455
vdup_dt = Untyped32;
1456
break;
1457
default:
1458
VIXL_UNREACHABLE();
1459
}
1460
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1461
vdup(cond, vdup_dt, rd, scratch);
1462
return;
1463
}
1464
if (dt.Is(F32) && neon_imm.CanConvert<float>()) {
1465
float f = neon_imm.GetImmediate<float>();
1466
// Punt to vmov.i32
1467
// TODO: The scope length was guessed based on the double case below. We
1468
// should analyse the worst-case size and add targetted tests.
1469
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
1470
vmov(cond, I32, rd, FloatToRawbits(f));
1471
return;
1472
}
1473
if (dt.Is(F64) && neon_imm.CanConvert<double>()) {
1474
// Punt to vmov.i64
1475
double d = neon_imm.GetImmediate<double>();
1476
// TODO: The scope length was measured empirically. We should analyse
1477
// the
1478
// worst-case size and add targetted tests.
1479
CodeBufferCheckScope scope(this, 6 * kMaxInstructionSizeInBytes);
1480
vmov(cond, I64, rd, DoubleToRawbits(d));
1481
return;
1482
}
1483
}
1484
}
1485
Assembler::Delegate(type, instruction, cond, dt, rd, operand);
1486
}
1487
1488
1489
void MacroAssembler::Delegate(InstructionType type,
1490
InstructionCondDtQQop instruction,
1491
Condition cond,
1492
DataType dt,
1493
QRegister rd,
1494
const QOperand& operand) {
1495
CONTEXT_SCOPE;
1496
if (type == kVmov) {
1497
if (operand.IsImmediate()) {
1498
const NeonImmediate& neon_imm = operand.GetNeonImmediate();
1499
switch (dt.GetValue()) {
1500
case I32:
1501
if (neon_imm.CanConvert<uint32_t>()) {
1502
uint32_t imm = neon_imm.GetImmediate<uint32_t>();
1503
// vmov.i32 d0, 0xabababab will translate into vmov.i8 d0, 0xab
1504
if (IsI8BitPattern(imm)) {
1505
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1506
vmov(cond, I8, rd, imm & 0xff);
1507
return;
1508
}
1509
// vmov.i32 d0, 0xff0000ff will translate into
1510
// vmov.i64 d0, 0xff0000ffff0000ff
1511
if (IsI64BitPattern(imm)) {
1512
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1513
vmov(cond, I64, rd, replicate<uint64_t>(imm));
1514
return;
1515
}
1516
// vmov.i32 d0, 0xffab0000 will translate into
1517
// vmvn.i32 d0, 0x0054ffff
1518
if (CanBeInverted(imm)) {
1519
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1520
vmvn(cond, I32, rd, ~imm);
1521
return;
1522
}
1523
}
1524
break;
1525
case I16:
1526
if (neon_imm.CanConvert<uint16_t>()) {
1527
uint16_t imm = neon_imm.GetImmediate<uint16_t>();
1528
// vmov.i16 d0, 0xabab will translate into vmov.i8 d0, 0xab
1529
if (IsI8BitPattern(imm)) {
1530
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1531
vmov(cond, I8, rd, imm & 0xff);
1532
return;
1533
}
1534
}
1535
break;
1536
case I64:
1537
if (neon_imm.CanConvert<uint64_t>()) {
1538
uint64_t imm = neon_imm.GetImmediate<uint64_t>();
1539
// vmov.i64 d0, -1 will translate into vmov.i8 d0, 0xff
1540
if (IsI8BitPattern(imm)) {
1541
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1542
vmov(cond, I8, rd, imm & 0xff);
1543
return;
1544
}
1545
// mov ip, lo(imm64)
1546
// vdup q0, ip
1547
// vdup is prefered to 'vmov d0[0]' as d0[1-3] don't need to be
1548
// preserved
1549
{
1550
UseScratchRegisterScope temps(this);
1551
Register scratch = temps.Acquire();
1552
{
1553
CodeBufferCheckScope scope(this,
1554
2 * kMaxInstructionSizeInBytes);
1555
mov(cond, scratch, static_cast<uint32_t>(imm & 0xffffffff));
1556
}
1557
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1558
vdup(cond, Untyped32, rd, scratch);
1559
}
1560
// mov ip, hi(imm64)
1561
// vmov.i32 d0[1], ip
1562
// vmov d1, d0
1563
{
1564
UseScratchRegisterScope temps(this);
1565
Register scratch = temps.Acquire();
1566
{
1567
CodeBufferCheckScope scope(this,
1568
2 * kMaxInstructionSizeInBytes);
1569
mov(cond, scratch, static_cast<uint32_t>(imm >> 32));
1570
}
1571
{
1572
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1573
vmov(cond,
1574
Untyped32,
1575
DRegisterLane(rd.GetLowDRegister(), 1),
1576
scratch);
1577
}
1578
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1579
vmov(cond, F64, rd.GetHighDRegister(), rd.GetLowDRegister());
1580
}
1581
return;
1582
}
1583
break;
1584
default:
1585
break;
1586
}
1587
VIXL_ASSERT(!dt.Is(I8)); // I8 cases should have been handled already.
1588
if ((dt.Is(I16) || dt.Is(I32)) && neon_imm.CanConvert<uint32_t>()) {
1589
// mov ip, imm32
1590
// vdup.16 d0, ip
1591
UseScratchRegisterScope temps(this);
1592
Register scratch = temps.Acquire();
1593
{
1594
CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
1595
mov(cond, scratch, neon_imm.GetImmediate<uint32_t>());
1596
}
1597
DataTypeValue vdup_dt = Untyped32;
1598
switch (dt.GetValue()) {
1599
case I16:
1600
vdup_dt = Untyped16;
1601
break;
1602
case I32:
1603
vdup_dt = Untyped32;
1604
break;
1605
default:
1606
VIXL_UNREACHABLE();
1607
}
1608
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1609
vdup(cond, vdup_dt, rd, scratch);
1610
return;
1611
}
1612
if (dt.Is(F32) && neon_imm.CanConvert<float>()) {
1613
// Punt to vmov.i64
1614
float f = neon_imm.GetImmediate<float>();
1615
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
1616
vmov(cond, I32, rd, FloatToRawbits(f));
1617
return;
1618
}
1619
if (dt.Is(F64) && neon_imm.CanConvert<double>()) {
1620
// Use vmov to create the double in the low D register, then duplicate
1621
// it into the high D register.
1622
double d = neon_imm.GetImmediate<double>();
1623
CodeBufferCheckScope scope(this, 7 * kMaxInstructionSizeInBytes);
1624
vmov(cond, F64, rd.GetLowDRegister(), d);
1625
vmov(cond, F64, rd.GetHighDRegister(), rd.GetLowDRegister());
1626
return;
1627
}
1628
}
1629
}
1630
Assembler::Delegate(type, instruction, cond, dt, rd, operand);
1631
}
1632
1633
1634
void MacroAssembler::Delegate(InstructionType type,
1635
InstructionCondRL instruction,
1636
Condition cond,
1637
Register rt,
1638
Location* location) {
1639
VIXL_ASSERT((type == kLdrb) || (type == kLdrh) || (type == kLdrsb) ||
1640
(type == kLdrsh));
1641
1642
CONTEXT_SCOPE;
1643
1644
if (location->IsBound()) {
1645
CodeBufferCheckScope scope(this, 5 * kMaxInstructionSizeInBytes);
1646
UseScratchRegisterScope temps(this);
1647
temps.Include(rt);
1648
Register scratch = temps.Acquire();
1649
uint32_t mask = GetOffsetMask(type, Offset);
1650
switch (type) {
1651
case kLdrb:
1652
ldrb(rt, MemOperandComputationHelper(cond, scratch, location, mask));
1653
return;
1654
case kLdrh:
1655
ldrh(rt, MemOperandComputationHelper(cond, scratch, location, mask));
1656
return;
1657
case kLdrsb:
1658
ldrsb(rt, MemOperandComputationHelper(cond, scratch, location, mask));
1659
return;
1660
case kLdrsh:
1661
ldrsh(rt, MemOperandComputationHelper(cond, scratch, location, mask));
1662
return;
1663
default:
1664
VIXL_UNREACHABLE();
1665
}
1666
return;
1667
}
1668
1669
Assembler::Delegate(type, instruction, cond, rt, location);
1670
}
1671
1672
1673
void MacroAssembler::Delegate(InstructionType type,
1674
InstructionCondRRL instruction,
1675
Condition cond,
1676
Register rt,
1677
Register rt2,
1678
Location* location) {
1679
VIXL_ASSERT(type == kLdrd);
1680
1681
CONTEXT_SCOPE;
1682
1683
if (location->IsBound()) {
1684
CodeBufferCheckScope scope(this, 6 * kMaxInstructionSizeInBytes);
1685
UseScratchRegisterScope temps(this);
1686
temps.Include(rt, rt2);
1687
Register scratch = temps.Acquire();
1688
uint32_t mask = GetOffsetMask(type, Offset);
1689
ldrd(rt, rt2, MemOperandComputationHelper(cond, scratch, location, mask));
1690
return;
1691
}
1692
1693
Assembler::Delegate(type, instruction, cond, rt, rt2, location);
1694
}
1695
1696
1697
void MacroAssembler::Delegate(InstructionType type,
1698
InstructionCondSizeRMop instruction,
1699
Condition cond,
1700
EncodingSize size,
1701
Register rd,
1702
const MemOperand& operand) {
1703
CONTEXT_SCOPE;
1704
VIXL_ASSERT(size.IsBest());
1705
VIXL_ASSERT((type == kLdr) || (type == kLdrb) || (type == kLdrh) ||
1706
(type == kLdrsb) || (type == kLdrsh) || (type == kStr) ||
1707
(type == kStrb) || (type == kStrh));
1708
if (operand.IsImmediate()) {
1709
const Register& rn = operand.GetBaseRegister();
1710
AddrMode addrmode = operand.GetAddrMode();
1711
int32_t offset = operand.GetOffsetImmediate();
1712
uint32_t extra_offset_mask = GetOffsetMask(type, addrmode);
1713
// Try to maximize the offset used by the MemOperand (load_store_offset).
1714
// Add the part which can't be used by the MemOperand (add_offset).
1715
uint32_t load_store_offset = offset & extra_offset_mask;
1716
uint32_t add_offset = offset & ~extra_offset_mask;
1717
if ((add_offset != 0) &&
1718
(IsModifiedImmediate(offset) || IsModifiedImmediate(-offset))) {
1719
load_store_offset = 0;
1720
add_offset = offset;
1721
}
1722
switch (addrmode) {
1723
case PreIndex:
1724
// Avoid the unpredictable case 'str r0, [r0, imm]!'
1725
if (!rn.Is(rd)) {
1726
// Pre-Indexed case:
1727
// ldr r0, [r1, 12345]! will translate into
1728
// add r1, r1, 12345
1729
// ldr r0, [r1]
1730
{
1731
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
1732
add(cond, rn, rn, add_offset);
1733
}
1734
{
1735
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1736
(this->*instruction)(cond,
1737
size,
1738
rd,
1739
MemOperand(rn, load_store_offset, PreIndex));
1740
}
1741
return;
1742
}
1743
break;
1744
case Offset: {
1745
UseScratchRegisterScope temps(this);
1746
// Allow using the destination as a scratch register if possible.
1747
if ((type != kStr) && (type != kStrb) && (type != kStrh) &&
1748
!rd.Is(rn)) {
1749
temps.Include(rd);
1750
}
1751
Register scratch = temps.Acquire();
1752
// Offset case:
1753
// ldr r0, [r1, 12345] will translate into
1754
// add r0, r1, 12345
1755
// ldr r0, [r0]
1756
{
1757
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
1758
add(cond, scratch, rn, add_offset);
1759
}
1760
{
1761
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1762
(this->*instruction)(cond,
1763
size,
1764
rd,
1765
MemOperand(scratch, load_store_offset));
1766
}
1767
return;
1768
}
1769
case PostIndex:
1770
// Avoid the unpredictable case 'ldr r0, [r0], imm'
1771
if (!rn.Is(rd)) {
1772
// Post-indexed case:
1773
// ldr r0. [r1], imm32 will translate into
1774
// ldr r0, [r1]
1775
// movw ip. imm32 & 0xffffffff
1776
// movt ip, imm32 >> 16
1777
// add r1, r1, ip
1778
{
1779
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1780
(this->*instruction)(cond,
1781
size,
1782
rd,
1783
MemOperand(rn, load_store_offset, PostIndex));
1784
}
1785
{
1786
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
1787
add(cond, rn, rn, add_offset);
1788
}
1789
return;
1790
}
1791
break;
1792
}
1793
} else if (operand.IsPlainRegister()) {
1794
const Register& rn = operand.GetBaseRegister();
1795
AddrMode addrmode = operand.GetAddrMode();
1796
const Register& rm = operand.GetOffsetRegister();
1797
if (rm.IsPC()) {
1798
VIXL_ABORT_WITH_MSG(
1799
"The MacroAssembler does not convert loads and stores with a PC "
1800
"offset register.\n");
1801
}
1802
if (rn.IsPC()) {
1803
if (addrmode == Offset) {
1804
if (IsUsingT32()) {
1805
VIXL_ABORT_WITH_MSG(
1806
"The MacroAssembler does not convert loads and stores with a PC "
1807
"base register for T32.\n");
1808
}
1809
} else {
1810
VIXL_ABORT_WITH_MSG(
1811
"The MacroAssembler does not convert loads and stores with a PC "
1812
"base register in pre-index or post-index mode.\n");
1813
}
1814
}
1815
switch (addrmode) {
1816
case PreIndex:
1817
// Avoid the unpredictable case 'str r0, [r0, imm]!'
1818
if (!rn.Is(rd)) {
1819
// Pre-Indexed case:
1820
// ldr r0, [r1, r2]! will translate into
1821
// add r1, r1, r2
1822
// ldr r0, [r1]
1823
{
1824
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1825
if (operand.GetSign().IsPlus()) {
1826
add(cond, rn, rn, rm);
1827
} else {
1828
sub(cond, rn, rn, rm);
1829
}
1830
}
1831
{
1832
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1833
(this->*instruction)(cond, size, rd, MemOperand(rn, Offset));
1834
}
1835
return;
1836
}
1837
break;
1838
case Offset: {
1839
UseScratchRegisterScope temps(this);
1840
// Allow using the destination as a scratch register if this is not a
1841
// store.
1842
// Avoid using PC as a temporary as this has side-effects.
1843
if ((type != kStr) && (type != kStrb) && (type != kStrh) &&
1844
!rd.IsPC()) {
1845
temps.Include(rd);
1846
}
1847
Register scratch = temps.Acquire();
1848
// Offset case:
1849
// ldr r0, [r1, r2] will translate into
1850
// add r0, r1, r2
1851
// ldr r0, [r0]
1852
{
1853
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1854
if (operand.GetSign().IsPlus()) {
1855
add(cond, scratch, rn, rm);
1856
} else {
1857
sub(cond, scratch, rn, rm);
1858
}
1859
}
1860
{
1861
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1862
(this->*instruction)(cond, size, rd, MemOperand(scratch, Offset));
1863
}
1864
return;
1865
}
1866
case PostIndex:
1867
// Avoid the unpredictable case 'ldr r0, [r0], imm'
1868
if (!rn.Is(rd)) {
1869
// Post-indexed case:
1870
// ldr r0. [r1], r2 will translate into
1871
// ldr r0, [r1]
1872
// add r1, r1, r2
1873
{
1874
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1875
(this->*instruction)(cond, size, rd, MemOperand(rn, Offset));
1876
}
1877
{
1878
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1879
if (operand.GetSign().IsPlus()) {
1880
add(cond, rn, rn, rm);
1881
} else {
1882
sub(cond, rn, rn, rm);
1883
}
1884
}
1885
return;
1886
}
1887
break;
1888
}
1889
}
1890
Assembler::Delegate(type, instruction, cond, size, rd, operand);
1891
}
1892
1893
1894
void MacroAssembler::Delegate(InstructionType type,
1895
InstructionCondRRMop instruction,
1896
Condition cond,
1897
Register rt,
1898
Register rt2,
1899
const MemOperand& operand) {
1900
if ((type == kLdaexd) || (type == kLdrexd) || (type == kStlex) ||
1901
(type == kStlexb) || (type == kStlexh) || (type == kStrex) ||
1902
(type == kStrexb) || (type == kStrexh)) {
1903
UnimplementedDelegate(type);
1904
return;
1905
}
1906
1907
VIXL_ASSERT((type == kLdrd) || (type == kStrd));
1908
1909
CONTEXT_SCOPE;
1910
1911
// TODO: Should we allow these cases?
1912
if (IsUsingA32()) {
1913
// The first register needs to be even.
1914
if ((rt.GetCode() & 1) != 0) {
1915
UnimplementedDelegate(type);
1916
return;
1917
}
1918
// Registers need to be adjacent.
1919
if (((rt.GetCode() + 1) % kNumberOfRegisters) != rt2.GetCode()) {
1920
UnimplementedDelegate(type);
1921
return;
1922
}
1923
// LDRD lr, pc [...] is not allowed.
1924
if (rt.Is(lr)) {
1925
UnimplementedDelegate(type);
1926
return;
1927
}
1928
}
1929
1930
if (operand.IsImmediate()) {
1931
const Register& rn = operand.GetBaseRegister();
1932
AddrMode addrmode = operand.GetAddrMode();
1933
int32_t offset = operand.GetOffsetImmediate();
1934
uint32_t extra_offset_mask = GetOffsetMask(type, addrmode);
1935
// Try to maximize the offset used by the MemOperand (load_store_offset).
1936
// Add the part which can't be used by the MemOperand (add_offset).
1937
uint32_t load_store_offset = offset & extra_offset_mask;
1938
uint32_t add_offset = offset & ~extra_offset_mask;
1939
if ((add_offset != 0) &&
1940
(IsModifiedImmediate(offset) || IsModifiedImmediate(-offset))) {
1941
load_store_offset = 0;
1942
add_offset = offset;
1943
}
1944
switch (addrmode) {
1945
case PreIndex: {
1946
// Allow using the destinations as a scratch registers if possible.
1947
UseScratchRegisterScope temps(this);
1948
if (type == kLdrd) {
1949
if (!rt.Is(rn)) temps.Include(rt);
1950
if (!rt2.Is(rn)) temps.Include(rt2);
1951
}
1952
1953
// Pre-Indexed case:
1954
// ldrd r0, r1, [r2, 12345]! will translate into
1955
// add r2, 12345
1956
// ldrd r0, r1, [r2]
1957
{
1958
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
1959
add(cond, rn, rn, add_offset);
1960
}
1961
{
1962
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1963
(this->*instruction)(cond,
1964
rt,
1965
rt2,
1966
MemOperand(rn, load_store_offset, PreIndex));
1967
}
1968
return;
1969
}
1970
case Offset: {
1971
UseScratchRegisterScope temps(this);
1972
// Allow using the destinations as a scratch registers if possible.
1973
if (type == kLdrd) {
1974
if (!rt.Is(rn)) temps.Include(rt);
1975
if (!rt2.Is(rn)) temps.Include(rt2);
1976
}
1977
Register scratch = temps.Acquire();
1978
// Offset case:
1979
// ldrd r0, r1, [r2, 12345] will translate into
1980
// add r0, r2, 12345
1981
// ldrd r0, r1, [r0]
1982
{
1983
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
1984
add(cond, scratch, rn, add_offset);
1985
}
1986
{
1987
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
1988
(this->*instruction)(cond,
1989
rt,
1990
rt2,
1991
MemOperand(scratch, load_store_offset));
1992
}
1993
return;
1994
}
1995
case PostIndex:
1996
// Avoid the unpredictable case 'ldrd r0, r1, [r0], imm'
1997
if (!rn.Is(rt) && !rn.Is(rt2)) {
1998
// Post-indexed case:
1999
// ldrd r0, r1, [r2], imm32 will translate into
2000
// ldrd r0, r1, [r2]
2001
// movw ip. imm32 & 0xffffffff
2002
// movt ip, imm32 >> 16
2003
// add r2, ip
2004
{
2005
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2006
(this->*instruction)(cond,
2007
rt,
2008
rt2,
2009
MemOperand(rn, load_store_offset, PostIndex));
2010
}
2011
{
2012
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
2013
add(cond, rn, rn, add_offset);
2014
}
2015
return;
2016
}
2017
break;
2018
}
2019
}
2020
if (operand.IsPlainRegister()) {
2021
const Register& rn = operand.GetBaseRegister();
2022
const Register& rm = operand.GetOffsetRegister();
2023
AddrMode addrmode = operand.GetAddrMode();
2024
switch (addrmode) {
2025
case PreIndex:
2026
// ldrd r0, r1, [r2, r3]! will translate into
2027
// add r2, r3
2028
// ldrd r0, r1, [r2]
2029
{
2030
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2031
if (operand.GetSign().IsPlus()) {
2032
add(cond, rn, rn, rm);
2033
} else {
2034
sub(cond, rn, rn, rm);
2035
}
2036
}
2037
{
2038
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2039
(this->*instruction)(cond, rt, rt2, MemOperand(rn, Offset));
2040
}
2041
return;
2042
case PostIndex:
2043
// ldrd r0, r1, [r2], r3 will translate into
2044
// ldrd r0, r1, [r2]
2045
// add r2, r3
2046
{
2047
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2048
(this->*instruction)(cond, rt, rt2, MemOperand(rn, Offset));
2049
}
2050
{
2051
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2052
if (operand.GetSign().IsPlus()) {
2053
add(cond, rn, rn, rm);
2054
} else {
2055
sub(cond, rn, rn, rm);
2056
}
2057
}
2058
return;
2059
case Offset: {
2060
UseScratchRegisterScope temps(this);
2061
// Allow using the destinations as a scratch registers if possible.
2062
if (type == kLdrd) {
2063
if (!rt.Is(rn)) temps.Include(rt);
2064
if (!rt2.Is(rn)) temps.Include(rt2);
2065
}
2066
Register scratch = temps.Acquire();
2067
// Offset case:
2068
// ldrd r0, r1, [r2, r3] will translate into
2069
// add r0, r2, r3
2070
// ldrd r0, r1, [r0]
2071
{
2072
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2073
if (operand.GetSign().IsPlus()) {
2074
add(cond, scratch, rn, rm);
2075
} else {
2076
sub(cond, scratch, rn, rm);
2077
}
2078
}
2079
{
2080
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2081
(this->*instruction)(cond, rt, rt2, MemOperand(scratch, Offset));
2082
}
2083
return;
2084
}
2085
}
2086
}
2087
Assembler::Delegate(type, instruction, cond, rt, rt2, operand);
2088
}
2089
2090
2091
void MacroAssembler::Delegate(InstructionType type,
2092
InstructionCondDtSMop instruction,
2093
Condition cond,
2094
DataType dt,
2095
SRegister rd,
2096
const MemOperand& operand) {
2097
CONTEXT_SCOPE;
2098
if (operand.IsImmediate()) {
2099
const Register& rn = operand.GetBaseRegister();
2100
AddrMode addrmode = operand.GetAddrMode();
2101
int32_t offset = operand.GetOffsetImmediate();
2102
VIXL_ASSERT(((offset > 0) && operand.GetSign().IsPlus()) ||
2103
((offset < 0) && operand.GetSign().IsMinus()) || (offset == 0));
2104
if (rn.IsPC()) {
2105
VIXL_ABORT_WITH_MSG(
2106
"The MacroAssembler does not convert vldr or vstr with a PC base "
2107
"register.\n");
2108
}
2109
switch (addrmode) {
2110
case PreIndex:
2111
// Pre-Indexed case:
2112
// vldr.32 s0, [r1, 12345]! will translate into
2113
// add r1, 12345
2114
// vldr.32 s0, [r1]
2115
if (offset != 0) {
2116
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
2117
add(cond, rn, rn, offset);
2118
}
2119
{
2120
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2121
(this->*instruction)(cond, dt, rd, MemOperand(rn, Offset));
2122
}
2123
return;
2124
case Offset: {
2125
UseScratchRegisterScope temps(this);
2126
Register scratch = temps.Acquire();
2127
// Offset case:
2128
// vldr.32 s0, [r1, 12345] will translate into
2129
// add ip, r1, 12345
2130
// vldr.32 s0, [ip]
2131
{
2132
VIXL_ASSERT(offset != 0);
2133
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
2134
add(cond, scratch, rn, offset);
2135
}
2136
{
2137
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2138
(this->*instruction)(cond, dt, rd, MemOperand(scratch, Offset));
2139
}
2140
return;
2141
}
2142
case PostIndex:
2143
// Post-indexed case:
2144
// vldr.32 s0, [r1], imm32 will translate into
2145
// vldr.32 s0, [r1]
2146
// movw ip. imm32 & 0xffffffff
2147
// movt ip, imm32 >> 16
2148
// add r1, ip
2149
{
2150
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2151
(this->*instruction)(cond, dt, rd, MemOperand(rn, Offset));
2152
}
2153
if (offset != 0) {
2154
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
2155
add(cond, rn, rn, offset);
2156
}
2157
return;
2158
}
2159
}
2160
Assembler::Delegate(type, instruction, cond, dt, rd, operand);
2161
}
2162
2163
2164
void MacroAssembler::Delegate(InstructionType type,
2165
InstructionCondDtDMop instruction,
2166
Condition cond,
2167
DataType dt,
2168
DRegister rd,
2169
const MemOperand& operand) {
2170
CONTEXT_SCOPE;
2171
if (operand.IsImmediate()) {
2172
const Register& rn = operand.GetBaseRegister();
2173
AddrMode addrmode = operand.GetAddrMode();
2174
int32_t offset = operand.GetOffsetImmediate();
2175
VIXL_ASSERT(((offset > 0) && operand.GetSign().IsPlus()) ||
2176
((offset < 0) && operand.GetSign().IsMinus()) || (offset == 0));
2177
if (rn.IsPC()) {
2178
VIXL_ABORT_WITH_MSG(
2179
"The MacroAssembler does not convert vldr or vstr with a PC base "
2180
"register.\n");
2181
}
2182
switch (addrmode) {
2183
case PreIndex:
2184
// Pre-Indexed case:
2185
// vldr.64 d0, [r1, 12345]! will translate into
2186
// add r1, 12345
2187
// vldr.64 d0, [r1]
2188
if (offset != 0) {
2189
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
2190
add(cond, rn, rn, offset);
2191
}
2192
{
2193
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2194
(this->*instruction)(cond, dt, rd, MemOperand(rn, Offset));
2195
}
2196
return;
2197
case Offset: {
2198
UseScratchRegisterScope temps(this);
2199
Register scratch = temps.Acquire();
2200
// Offset case:
2201
// vldr.64 d0, [r1, 12345] will translate into
2202
// add ip, r1, 12345
2203
// vldr.32 s0, [ip]
2204
{
2205
VIXL_ASSERT(offset != 0);
2206
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
2207
add(cond, scratch, rn, offset);
2208
}
2209
{
2210
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2211
(this->*instruction)(cond, dt, rd, MemOperand(scratch, Offset));
2212
}
2213
return;
2214
}
2215
case PostIndex:
2216
// Post-indexed case:
2217
// vldr.64 d0. [r1], imm32 will translate into
2218
// vldr.64 d0, [r1]
2219
// movw ip. imm32 & 0xffffffff
2220
// movt ip, imm32 >> 16
2221
// add r1, ip
2222
{
2223
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2224
(this->*instruction)(cond, dt, rd, MemOperand(rn, Offset));
2225
}
2226
if (offset != 0) {
2227
CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
2228
add(cond, rn, rn, offset);
2229
}
2230
return;
2231
}
2232
}
2233
Assembler::Delegate(type, instruction, cond, dt, rd, operand);
2234
}
2235
2236
2237
void MacroAssembler::Delegate(InstructionType type,
2238
InstructionCondMsrOp instruction,
2239
Condition cond,
2240
MaskedSpecialRegister spec_reg,
2241
const Operand& operand) {
2242
USE(type);
2243
VIXL_ASSERT(type == kMsr);
2244
if (operand.IsImmediate()) {
2245
UseScratchRegisterScope temps(this);
2246
Register scratch = temps.Acquire();
2247
{
2248
CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
2249
mov(cond, scratch, operand);
2250
}
2251
CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
2252
msr(cond, spec_reg, scratch);
2253
return;
2254
}
2255
Assembler::Delegate(type, instruction, cond, spec_reg, operand);
2256
}
2257
2258
2259
void MacroAssembler::Delegate(InstructionType type,
2260
InstructionCondDtDL instruction,
2261
Condition cond,
2262
DataType dt,
2263
DRegister rd,
2264
Location* location) {
2265
VIXL_ASSERT(type == kVldr);
2266
2267
CONTEXT_SCOPE;
2268
2269
if (location->IsBound()) {
2270
CodeBufferCheckScope scope(this, 5 * kMaxInstructionSizeInBytes);
2271
UseScratchRegisterScope temps(this);
2272
Register scratch = temps.Acquire();
2273
uint32_t mask = GetOffsetMask(type, Offset);
2274
vldr(dt, rd, MemOperandComputationHelper(cond, scratch, location, mask));
2275
return;
2276
}
2277
2278
Assembler::Delegate(type, instruction, cond, dt, rd, location);
2279
}
2280
2281
2282
void MacroAssembler::Delegate(InstructionType type,
2283
InstructionCondDtSL instruction,
2284
Condition cond,
2285
DataType dt,
2286
SRegister rd,
2287
Location* location) {
2288
VIXL_ASSERT(type == kVldr);
2289
2290
CONTEXT_SCOPE;
2291
2292
if (location->IsBound()) {
2293
CodeBufferCheckScope scope(this, 5 * kMaxInstructionSizeInBytes);
2294
UseScratchRegisterScope temps(this);
2295
Register scratch = temps.Acquire();
2296
uint32_t mask = GetOffsetMask(type, Offset);
2297
vldr(dt, rd, MemOperandComputationHelper(cond, scratch, location, mask));
2298
return;
2299
}
2300
2301
Assembler::Delegate(type, instruction, cond, dt, rd, location);
2302
}
2303
2304
2305
#undef CONTEXT_SCOPE
2306
#undef TOSTRING
2307
#undef STRINGIFY
2308
2309
// Start of generated code.
2310
// End of generated code.
2311
} // namespace aarch32
2312
} // namespace vixl
2313
2314