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